| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| //===-- Implementation of atof --------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/stdlib/atof.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/str_to_float.h" | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| LLVM_LIBC_FUNCTION(double, atof, (const char *str)) { | ||
| return internal::strtofloatingpoint<double>(str, nullptr); | ||
| } | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| //===-- Implementation header for atof --------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_STDLIB_ATOF_H | ||
| #define LLVM_LIBC_SRC_STDLIB_ATOF_H | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| double atof(const char *str); | ||
|
|
||
| } // namespace __llvm_libc | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDLIB_ATOI_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //===-- Implementation of strtod ------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/stdlib/strtod.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/str_to_float.h" | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| LLVM_LIBC_FUNCTION(double, strtod, | ||
| (const char *__restrict str, char **__restrict str_end)) { | ||
| return internal::strtofloatingpoint<double>(str, str_end); | ||
| } | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| //===-- Implementation header for strtod ------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_STDLIB_STRTOD_H | ||
| #define LLVM_LIBC_SRC_STDLIB_STRTOD_H | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| double strtod(const char *__restrict str, char **__restrict str_end); | ||
|
|
||
| } // namespace __llvm_libc | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDLIB_STRTOD_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| //===-- Implementation of strtof ------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/stdlib/strtof.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/str_to_float.h" | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| LLVM_LIBC_FUNCTION(float, strtof, | ||
| (const char *__restrict str, char **__restrict str_end)) { | ||
| return internal::strtofloatingpoint<float>(str, str_end); | ||
| } | ||
|
|
||
| } // namespace __llvm_libc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| //===-- Implementation header for strtof ------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_STDLIB_STRTOF_H | ||
| #define LLVM_LIBC_SRC_STDLIB_STRTOF_H | ||
|
|
||
| namespace __llvm_libc { | ||
|
|
||
| float strtof(const char *__restrict str, char **__restrict str_end); | ||
|
|
||
| } // namespace __llvm_libc | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDLIB_STRTOF_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # This is the sample data from https://github.com/nigeltao/parse-number-fxx-test-data | ||
| 3C00 3F800000 3FF0000000000000 1 | ||
| 3D00 3FA00000 3FF4000000000000 1.25 | ||
| 3D9A 3FB33333 3FF6666666666666 1.4 | ||
| 57B7 42F6E979 405EDD2F1A9FBE77 123.456 | ||
| 622A 44454000 4088A80000000000 789 | ||
| 7C00 7F800000 7FF0000000000000 123.456e789 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| //===-- strtofloatingpoint comparison test --------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| // #include "src/__support/str_float_conv_utils.h" | ||
|
|
||
| #include <stdlib.h> | ||
|
|
||
| // #include "src/__support/FPUtil/FPBits.h" | ||
|
|
||
| #include <fstream> | ||
| #include <iostream> | ||
| #include <string> | ||
|
|
||
| // The intent of this test is to read in files in the format used in this test | ||
| // dataset: https://github.com/nigeltao/parse-number-fxx-test-data | ||
| // The format is as follows: | ||
| // Hexadecimal representations of IEEE754 floats in 16 bits, 32 bits, and 64 | ||
| // bits, then the string that matches to them. | ||
|
|
||
| // 3C00 3F800000 3FF0000000000000 1.0 | ||
|
|
||
| // By default, float_comp_in.txt is used as the test set, but once built this | ||
| // file can be run against the larger test set. To do that, clone the repository | ||
| // with the dataset, then navigate to the compiled binary of this file (it | ||
| // should be in llvm_project/build/bin). Run the following command: | ||
| // ./libc_str_to_float_comparison_test <path/to/dataset/repo>/data/* | ||
| // It will take a few seconds to run. | ||
|
|
||
| static inline uint32_t hexCharToU32(char in) { | ||
| return in > '9' ? in + 10 - 'A' : in - '0'; | ||
| } | ||
|
|
||
| // Fast because it assumes inStr points to exactly 8 uppercase hex chars | ||
| static inline uint32_t fastHexToU32(const char *inStr) { | ||
| uint32_t result = 0; | ||
| result = (hexCharToU32(inStr[0]) << 28) + (hexCharToU32(inStr[1]) << 24) + | ||
| (hexCharToU32(inStr[2]) << 20) + (hexCharToU32(inStr[3]) << 16) + | ||
| (hexCharToU32(inStr[4]) << 12) + (hexCharToU32(inStr[5]) << 8) + | ||
| (hexCharToU32(inStr[6]) << 4) + hexCharToU32(inStr[7]); | ||
| return result; | ||
| } | ||
|
|
||
| // Fast because it assumes inStr points to exactly 8 uppercase hex chars | ||
| static inline uint64_t fastHexToU64(const char *inStr) { | ||
| uint64_t result = 0; | ||
| result = (static_cast<uint64_t>(fastHexToU32(inStr)) << 32) + | ||
| fastHexToU32(inStr + 8); | ||
| return result; | ||
| } | ||
|
|
||
| int checkFile(char *inputFileName, int *totalFails, int *totalBitDiffs, | ||
| int *detailedBitDiffs, int *total) { | ||
| int32_t curFails = 0; // Only counts actual failures, not bitdiffs. | ||
| int32_t curBitDiffs = 0; // A bitdiff is when the expected result and actual | ||
| // result are off by +/- 1 bit. | ||
| std::string line; | ||
| std::string num; | ||
|
|
||
| std::ifstream fileStream(inputFileName, std::ifstream::in); | ||
|
|
||
| if (!fileStream.is_open()) { | ||
| std::cout << "file '" << inputFileName << "' failed to open. Exiting.\n"; | ||
| return 1; | ||
| } | ||
| while (getline(fileStream, line)) { | ||
| if (line[0] == '#') { | ||
| continue; | ||
| } | ||
| *total = *total + 1; | ||
| uint32_t expectedFloatRaw; | ||
| uint64_t expectedDoubleRaw; | ||
|
|
||
| expectedFloatRaw = fastHexToU32(line.c_str() + 5); | ||
| expectedDoubleRaw = fastHexToU64(line.c_str() + 14); | ||
| num = line.substr(31); | ||
|
|
||
| float floatResult = strtof(num.c_str(), nullptr); | ||
|
|
||
| double doubleResult = strtod(num.c_str(), nullptr); | ||
|
|
||
| uint32_t floatRaw = *(uint32_t *)(&floatResult); | ||
|
|
||
| uint64_t doubleRaw = *(uint64_t *)(&doubleResult); | ||
|
|
||
| if (!(expectedFloatRaw == floatRaw)) { | ||
| if (expectedFloatRaw == floatRaw + 1 || | ||
| expectedFloatRaw == floatRaw - 1) { | ||
| curBitDiffs++; | ||
| if (expectedFloatRaw == floatRaw + 1) { | ||
| detailedBitDiffs[0] = detailedBitDiffs[0] + 1; // float low | ||
| } else { | ||
| detailedBitDiffs[1] = detailedBitDiffs[1] + 1; // float high | ||
| } | ||
| } else { | ||
| curFails++; | ||
| } | ||
| if (curFails + curBitDiffs < 10) { | ||
| std::cout << "Float fail for '" << num << "'. Expected " << std::hex | ||
| << expectedFloatRaw << " but got " << floatRaw << "\n" | ||
| << std::dec; | ||
| } | ||
| } | ||
|
|
||
| if (!(expectedDoubleRaw == doubleRaw)) { | ||
| if (expectedDoubleRaw == doubleRaw + 1 || | ||
| expectedDoubleRaw == doubleRaw - 1) { | ||
| curBitDiffs++; | ||
| if (expectedDoubleRaw == doubleRaw + 1) { | ||
| detailedBitDiffs[2] = detailedBitDiffs[2] + 1; // double low | ||
| } else { | ||
| detailedBitDiffs[3] = detailedBitDiffs[3] + 1; // double high | ||
| } | ||
| } else { | ||
| curFails++; | ||
| } | ||
| if (curFails + curBitDiffs < 10) { | ||
| std::cout << "Double fail for '" << num << "'. Expected " << std::hex | ||
| << expectedDoubleRaw << " but got " << doubleRaw << "\n" | ||
| << std::dec; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fileStream.close(); | ||
|
|
||
| *totalBitDiffs += curBitDiffs; | ||
| *totalFails += curFails; | ||
|
|
||
| if (curFails > 1 || curBitDiffs > 1) { | ||
| return 2; | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| int main(int argc, char *argv[]) { | ||
| int result = 0; | ||
| int fails = 0; | ||
|
|
||
| // Bitdiffs are cases where the expected result and actual result only differ | ||
| // by +/- the least significant bit. They are tracked seperately from larger | ||
| // failures since a bitdiff is most likely the result of a rounding error, and | ||
| // splitting them off makes them easier to track down. | ||
| int bitdiffs = 0; | ||
| int detailedBitDiffs[4] = {0, 0, 0, 0}; | ||
|
|
||
| int total = 0; | ||
| for (int i = 1; i < argc; i++) { | ||
| std::cout << "Starting file " << argv[i] << "\n"; | ||
| int curResult = | ||
| checkFile(argv[i], &fails, &bitdiffs, detailedBitDiffs, &total); | ||
| if (curResult == 1) { | ||
| result = 1; | ||
| break; | ||
| } else if (curResult == 2) { | ||
| result = 2; | ||
| } | ||
| } | ||
| std::cout << "Results:\n" | ||
| << "Total significant failed conversions: " << fails << "\n" | ||
| << "Total conversions off by +/- 1 bit: " << bitdiffs << "\n" | ||
| << "\t" << detailedBitDiffs[0] << "\tfloat low\n" | ||
| << "\t" << detailedBitDiffs[1] << "\tfloat high\n" | ||
| << "\t" << detailedBitDiffs[2] << "\tdouble low\n" | ||
| << "\t" << detailedBitDiffs[3] << "\tdouble high\n" | ||
| << "Total lines: " << total << "\n"; | ||
| return result; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| //===-- Unittests for str_to_float ----------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/__support/str_to_float.h" | ||
|
|
||
| #include "utils/UnitTest/Test.h" | ||
|
|
||
| class LlvmLibcStrToFloatTest : public __llvm_libc::testing::Test { | ||
| public: | ||
| template <class T> | ||
| void EiselLemireTest( | ||
| const typename __llvm_libc::fputil::FPBits<T>::UIntType inputMantissa, | ||
| const int32_t inputExp10, | ||
| const typename __llvm_libc::fputil::FPBits<T>::UIntType | ||
| expectedOutputMantissa, | ||
| const uint32_t expectedOutputExp2) { | ||
| typename __llvm_libc::fputil::FPBits<T>::UIntType actualOutputMantissa = 0; | ||
| uint32_t actualOutputExp2 = 0; | ||
|
|
||
| ASSERT_TRUE(__llvm_libc::internal::eiselLemire<T>( | ||
| inputMantissa, inputExp10, &actualOutputMantissa, &actualOutputExp2)); | ||
| EXPECT_EQ(actualOutputMantissa, expectedOutputMantissa); | ||
| EXPECT_EQ(actualOutputExp2, expectedOutputExp2); | ||
| } | ||
|
|
||
| template <class T> | ||
| void SimpleDecimalConversionTest( | ||
| const char *__restrict numStart, | ||
| const typename __llvm_libc::fputil::FPBits<T>::UIntType | ||
| expectedOutputMantissa, | ||
| const uint32_t expectedOutputExp2, const int expectedErrno = 0) { | ||
| typename __llvm_libc::fputil::FPBits<T>::UIntType actualOutputMantissa = 0; | ||
| uint32_t actualOutputExp2 = 0; | ||
| errno = 0; | ||
|
|
||
| __llvm_libc::internal::simpleDecimalConversion<T>( | ||
| numStart, &actualOutputMantissa, &actualOutputExp2); | ||
| EXPECT_EQ(actualOutputMantissa, expectedOutputMantissa); | ||
| EXPECT_EQ(actualOutputExp2, expectedOutputExp2); | ||
| EXPECT_EQ(errno, expectedErrno); | ||
| } | ||
| }; | ||
|
|
||
| TEST(LlvmLibcStrToFloatTest, LeadingZeroes) { | ||
| uint64_t testNum64 = 1; | ||
| uint32_t numOfZeroes = 63; | ||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint64_t>(0), 64u); | ||
| for (; numOfZeroes < 64; testNum64 <<= 1, numOfZeroes--) { | ||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint64_t>(testNum64), | ||
| numOfZeroes); | ||
| } | ||
|
|
||
| testNum64 = 3; | ||
| numOfZeroes = 62; | ||
| for (; numOfZeroes > 63; testNum64 <<= 1, numOfZeroes--) { | ||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint64_t>(testNum64), | ||
| numOfZeroes); | ||
| } | ||
|
|
||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint64_t>(0xffffffffffffffff), | ||
| 0u); | ||
|
|
||
| testNum64 = 1; | ||
| numOfZeroes = 63; | ||
| for (; numOfZeroes > 63; testNum64 = (testNum64 << 1) + 1, numOfZeroes--) { | ||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint64_t>(testNum64), | ||
| numOfZeroes); | ||
| } | ||
|
|
||
| uint64_t testNum32 = 1; | ||
| numOfZeroes = 31; | ||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint32_t>(0), 32u); | ||
| for (; numOfZeroes < 32; testNum32 <<= 1, numOfZeroes--) { | ||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint32_t>(testNum32), | ||
| numOfZeroes); | ||
| } | ||
|
|
||
| EXPECT_EQ(__llvm_libc::internal::leadingZeroes<uint32_t>(0xffffffff), 0u); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat64Simple) { | ||
| EiselLemireTest<double>(12345678901234567890u, 1, 0x1AC53A7E04BCDA, 1089); | ||
| EiselLemireTest<double>(123, 0, 0x1EC00000000000, 1029); | ||
| EiselLemireTest<double>(12345678901234568192u, 0, 0x156A95319D63E2, 1086); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat64SpecificFailures) { | ||
| // These test cases have caused failures in the past. | ||
| EiselLemireTest<double>(358416272, -33, 0x1BBB2A68C9D0B9, 941); | ||
| EiselLemireTest<double>(2166568064000000238u, -9, 0x10246690000000, 1054); | ||
| EiselLemireTest<double>(2794967654709307187u, 1, 0x183e132bc608c8, 1087); | ||
| EiselLemireTest<double>(2794967654709307188u, 1, 0x183e132bc608c9, 1087); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, EiselLemireFallbackStates) { | ||
| // Check the fallback states for the algorithm: | ||
| uint32_t floatOutputMantissa = 0; | ||
| uint64_t doubleOutputMantissa = 0; | ||
| __uint128_t tooLongMantissa = 0; | ||
| uint32_t outputExp2 = 0; | ||
|
|
||
| // This Eisel-Lemire implementation doesn't support long doubles yet. | ||
| ASSERT_FALSE(__llvm_libc::internal::eiselLemire<long double>( | ||
| tooLongMantissa, 0, &tooLongMantissa, &outputExp2)); | ||
|
|
||
| // This number can't be evaluated by Eisel-Lemire since it's exactly 1024 away | ||
| // from both of its closest floating point approximations | ||
| // (12345678901234548736 and 12345678901234550784) | ||
| ASSERT_FALSE(__llvm_libc::internal::eiselLemire<double>( | ||
| 12345678901234549760u, 0, &doubleOutputMantissa, &outputExp2)); | ||
|
|
||
| ASSERT_FALSE(__llvm_libc::internal::eiselLemire<float>( | ||
| 20040229, 0, &floatOutputMantissa, &outputExp2)); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicWholeNumbers) { | ||
| SimpleDecimalConversionTest<double>("123456789012345678900", 0x1AC53A7E04BCDA, | ||
| 1089); | ||
| SimpleDecimalConversionTest<double>("123", 0x1EC00000000000, 1029); | ||
| SimpleDecimalConversionTest<double>("12345678901234549760", 0x156A95319D63D8, | ||
| 1086); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicDecimals) { | ||
| SimpleDecimalConversionTest<double>("1.2345", 0x13c083126e978d, 1023); | ||
| SimpleDecimalConversionTest<double>(".2345", 0x1e04189374bc6a, 1020); | ||
| SimpleDecimalConversionTest<double>(".299792458", 0x132fccb4aca314, 1021); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicExponents) { | ||
| SimpleDecimalConversionTest<double>("1e10", 0x12a05f20000000, 1056); | ||
| SimpleDecimalConversionTest<double>("1e-10", 0x1b7cdfd9d7bdbb, 989); | ||
| SimpleDecimalConversionTest<double>("1e300", 0x17e43c8800759c, 2019); | ||
| SimpleDecimalConversionTest<double>("1e-300", 0x156e1fc2f8f359, 26); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicSubnormals) { | ||
| SimpleDecimalConversionTest<double>("1e-320", 0x7e8, 0); | ||
| SimpleDecimalConversionTest<double>("1e-308", 0x730d67819e8d2, 0); | ||
| SimpleDecimalConversionTest<double>("2.9e-308", 0x14da6df5e4bcc8, 1); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64SubnormalRounding) { | ||
|
|
||
| // Technically you can keep adding digits until you hit the truncation limit, | ||
| // but this is the shortest string that results in the maximum subnormal that | ||
| // I found. | ||
| SimpleDecimalConversionTest<double>("2.225073858507201e-308", 0xfffffffffffff, | ||
| 0); | ||
|
|
||
| // Same here, if you were to extend the max subnormal out for another 800 | ||
| // digits, incrementing any one of those digits would create a normal number. | ||
| SimpleDecimalConversionTest<double>("2.2250738585072012e-308", | ||
| 0x10000000000000, 1); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion32SpecificFailures) { | ||
| SimpleDecimalConversionTest<float>( | ||
| "1.4012984643248170709237295832899161312802619418765e-45", 0x1, 0); | ||
| } | ||
|
|
||
| TEST(LlvmLibcStrToFloatTest, SimpleDecimalConversionExtraTypes) { | ||
| uint32_t floatOutputMantissa = 0; | ||
| uint32_t outputExp2 = 0; | ||
|
|
||
| errno = 0; | ||
| __llvm_libc::internal::simpleDecimalConversion<float>( | ||
| "123456789012345678900", &floatOutputMantissa, &outputExp2); | ||
| EXPECT_EQ(floatOutputMantissa, uint32_t(0xd629d4)); | ||
| EXPECT_EQ(outputExp2, uint32_t(193)); | ||
| EXPECT_EQ(errno, 0); | ||
|
|
||
| uint64_t doubleOutputMantissa = 0; | ||
| outputExp2 = 0; | ||
|
|
||
| errno = 0; | ||
| __llvm_libc::internal::simpleDecimalConversion<double>( | ||
| "123456789012345678900", &doubleOutputMantissa, &outputExp2); | ||
| EXPECT_EQ(doubleOutputMantissa, uint64_t(0x1AC53A7E04BCDA)); | ||
| EXPECT_EQ(outputExp2, uint32_t(1089)); | ||
| EXPECT_EQ(errno, 0); | ||
|
|
||
| // TODO(michaelrj): Get long double support working. | ||
|
|
||
| // __uint128_t longDoubleOutputMantissa = 0; | ||
| // outputExp2 = 0; | ||
|
|
||
| // errno = 0; | ||
| // __llvm_libc::internal::simpleDecimalConversion<long double>( | ||
| // "123456789012345678900", &longDoubleOutputMantissa, &outputExp2); | ||
| // EXPECT_EQ(longDoubleOutputMantissa, __uint128_t(0x1AC53A7E04BCDA)); | ||
| // EXPECT_EQ(outputExp2, uint32_t(1089)); | ||
| // EXPECT_EQ(errno, 0); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| //===-- Unittests for atof ------------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/stdlib/atof.h" | ||
|
|
||
| #include "utils/UnitTest/Test.h" | ||
|
|
||
| #include <errno.h> | ||
| #include <limits.h> | ||
| #include <stddef.h> | ||
|
|
||
| // This is just a simple test to make sure that this function works at all. It's | ||
| // functionally identical to strtod so the bulk of the testing is there. | ||
| TEST(LlvmLibcAToFTest, SimpleTest) { | ||
| __llvm_libc::fputil::FPBits<double> expectedFP = | ||
| __llvm_libc::fputil::FPBits<double>(uint64_t(0x405ec00000000000)); | ||
|
|
||
| errno = 0; | ||
| double result = __llvm_libc::atof("123"); | ||
|
|
||
| __llvm_libc::fputil::FPBits<double> actualFP = | ||
| __llvm_libc::fputil::FPBits<double>(result); | ||
|
|
||
| EXPECT_EQ(actualFP.bits, expectedFP.bits); | ||
| EXPECT_EQ(actualFP.getSign(), expectedFP.getSign()); | ||
| EXPECT_EQ(actualFP.getExponent(), expectedFP.getExponent()); | ||
| EXPECT_EQ(actualFP.getMantissa(), expectedFP.getMantissa()); | ||
| EXPECT_EQ(errno, 0); | ||
| } | ||
|
|
||
| TEST(LlvmLibcAToFTest, FailedParsingTest) { | ||
| __llvm_libc::fputil::FPBits<double> expectedFP = | ||
| __llvm_libc::fputil::FPBits<double>(uint64_t(0)); | ||
|
|
||
| errno = 0; | ||
| double result = __llvm_libc::atof("???"); | ||
|
|
||
| __llvm_libc::fputil::FPBits<double> actualFP = | ||
| __llvm_libc::fputil::FPBits<double>(result); | ||
|
|
||
| EXPECT_EQ(actualFP.bits, expectedFP.bits); | ||
| EXPECT_EQ(actualFP.getSign(), expectedFP.getSign()); | ||
| EXPECT_EQ(actualFP.getExponent(), expectedFP.getExponent()); | ||
| EXPECT_EQ(actualFP.getMantissa(), expectedFP.getMantissa()); | ||
| EXPECT_EQ(errno, 0); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| //===-- Unittests for strtod ---------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/stdlib/strtod.h" | ||
|
|
||
| #include "utils/UnitTest/Test.h" | ||
|
|
||
| #include <errno.h> | ||
| #include <limits.h> | ||
| #include <stddef.h> | ||
|
|
||
| class LlvmLibcStrToDTest : public __llvm_libc::testing::Test { | ||
| public: | ||
| void runTest(const char *inputString, const ptrdiff_t expectedStrLen, | ||
| const uint64_t expectedRawData, const int expectedErrno = 0) { | ||
| // expectedRawData is the expected double result as a uint64_t, organized | ||
| // according to IEEE754: | ||
| // | ||
| // +-- 1 Sign Bit +-- 52 Mantissa bits | ||
| // | | | ||
| // | +-------------------------+------------------------+ | ||
| // | | | | ||
| // SEEEEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM | ||
| // | | | ||
| // +----+----+ | ||
| // | | ||
| // +-- 11 Exponent Bits | ||
| // | ||
| // This is so that the result can be compared in parts. | ||
| char *strEnd = nullptr; | ||
|
|
||
| __llvm_libc::fputil::FPBits<double> expectedFP = | ||
| __llvm_libc::fputil::FPBits<double>(expectedRawData); | ||
|
|
||
| errno = 0; | ||
| double result = __llvm_libc::strtod(inputString, &strEnd); | ||
|
|
||
| __llvm_libc::fputil::FPBits<double> actualFP = | ||
| __llvm_libc::fputil::FPBits<double>(result); | ||
|
|
||
| EXPECT_EQ(strEnd - inputString, expectedStrLen); | ||
|
|
||
| EXPECT_EQ(actualFP.bits, expectedFP.bits); | ||
| EXPECT_EQ(actualFP.getSign(), expectedFP.getSign()); | ||
| EXPECT_EQ(actualFP.getExponent(), expectedFP.getExponent()); | ||
| EXPECT_EQ(actualFP.getMantissa(), expectedFP.getMantissa()); | ||
| EXPECT_EQ(errno, expectedErrno); | ||
| } | ||
| }; | ||
|
|
||
| TEST_F(LlvmLibcStrToDTest, SimpleTest) { | ||
| runTest("123", 3, uint64_t(0x405ec00000000000)); | ||
|
|
||
| // This should fail on Eisel-Lemire, forcing a fallback to simple decimal | ||
| // conversion. | ||
| runTest("12345678901234549760", 20, uint64_t(0x43e56a95319d63d8)); | ||
|
|
||
| // Found while looking for difficult test cases here: | ||
| // https://github.com/nigeltao/parse-number-fxx-test-data/blob/main/more-test-cases/golang-org-issue-36657.txt | ||
| runTest("1090544144181609348835077142190", 31, uint64_t(0x462b8779f2474dfb)); | ||
|
|
||
| runTest("0x123", 5, uint64_t(0x4072300000000000)); | ||
| } | ||
|
|
||
| // These are tests that have caused problems in the past. | ||
| TEST_F(LlvmLibcStrToDTest, SpecificFailures) { | ||
| runTest("3E70000000000000", 16, uint64_t(0x7FF0000000000000), ERANGE); | ||
| runTest("358416272e-33", 13, uint64_t(0x3adbbb2a68c9d0b9)); | ||
| runTest("2.16656806400000023841857910156251e9", 36, | ||
| uint64_t(0x41e0246690000001)); | ||
| runTest("27949676547093071875", 20, uint64_t(0x43f83e132bc608c9)); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToDTest, FuzzFailures) { | ||
| runTest("-\xff\xff\xff\xff\xff\xff\xff\x01", 0, uint64_t(0)); | ||
| runTest("-.????", 0, uint64_t(0)); | ||
| runTest("44444444444444444444444444444444444444444444444444A44444444444444444" | ||
| "44444444444*\x99\xff\xff\xff\xff", | ||
| 50, uint64_t(0x4a3e68fdd0e0b2d8)); | ||
| runTest("-NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNKNNNNNNNNNNNNNNNNNN?" | ||
| "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN?", | ||
| 0, uint64_t(0)); | ||
| runTest("0x.666E40", 9, uint64_t(0x3fd99b9000000000)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| //===-- Unittests for strtof ---------------------------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/__support/FPUtil/FPBits.h" | ||
| #include "src/stdlib/strtof.h" | ||
|
|
||
| #include "utils/UnitTest/Test.h" | ||
|
|
||
| #include <errno.h> | ||
| #include <limits.h> | ||
| #include <stddef.h> | ||
|
|
||
| class LlvmLibcStrToFTest : public __llvm_libc::testing::Test { | ||
| public: | ||
| void runTest(const char *inputString, const ptrdiff_t expectedStrLen, | ||
| const uint32_t expectedRawData, const int expectedErrno = 0) { | ||
| // expectedRawData is the expected float result as a uint32_t, organized | ||
| // according to IEEE754: | ||
| // | ||
| // +-- 1 Sign Bit +-- 23 Mantissa bits | ||
| // | | | ||
| // | +----------+----------+ | ||
| // | | | | ||
| // SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM | ||
| // | | | ||
| // +--+---+ | ||
| // | | ||
| // +-- 8 Exponent Bits | ||
| // | ||
| // This is so that the result can be compared in parts. | ||
| char *strEnd = nullptr; | ||
|
|
||
| __llvm_libc::fputil::FPBits<float> expectedFP = | ||
| __llvm_libc::fputil::FPBits<float>(expectedRawData); | ||
|
|
||
| errno = 0; | ||
| float result = __llvm_libc::strtof(inputString, &strEnd); | ||
|
|
||
| __llvm_libc::fputil::FPBits<float> actualFP = | ||
| __llvm_libc::fputil::FPBits<float>(result); | ||
|
|
||
| EXPECT_EQ(strEnd - inputString, expectedStrLen); | ||
|
|
||
| EXPECT_EQ(actualFP.bits, expectedFP.bits); | ||
| EXPECT_EQ(actualFP.getSign(), expectedFP.getSign()); | ||
| EXPECT_EQ(actualFP.getExponent(), expectedFP.getExponent()); | ||
| EXPECT_EQ(actualFP.getMantissa(), expectedFP.getMantissa()); | ||
| EXPECT_EQ(errno, expectedErrno); | ||
| } | ||
| }; | ||
|
|
||
| // This is the set of tests that I have working (verified correct when compared | ||
| // to system libc). This is here so I don't break more things when I try to fix | ||
| // them. | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, BasicDecimalTests) { | ||
| runTest("1", 1, 0x3f800000); | ||
| runTest("123", 3, 0x42f60000); | ||
| runTest("1234567890", 10, 0x4e932c06u); | ||
| runTest("123456789012345678901", 21, 0x60d629d4); | ||
| runTest("0.1", 3, 0x3dcccccdu); | ||
| runTest(".1", 2, 0x3dcccccdu); | ||
| runTest("-0.123456789", 12, 0xbdfcd6eau); | ||
| runTest("0.11111111111111111111", 22, 0x3de38e39u); | ||
| runTest("0.0000000000000000000000001", 27, 0x15f79688u); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, DecimalOutOfRangeTests) { | ||
| runTest("555E36", 6, 0x7f800000, ERANGE); | ||
| runTest("1e-10000", 8, 0x0, ERANGE); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, DecimalsWithRoundingProblems) { | ||
| runTest("20040229", 8, 0x4b98e512); | ||
| runTest("20040401", 8, 0x4b98e568); | ||
| runTest("9E9", 3, 0x50061c46); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, DecimalSubnormals) { | ||
| runTest("1.4012984643248170709237295832899161312802619418765e-45", 55, 0x1); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, DecimalWithLongExponent) { | ||
| runTest("1e2147483648", 12, 0x7f800000, ERANGE); | ||
| runTest("1e2147483646", 12, 0x7f800000, ERANGE); | ||
| runTest("100e2147483646", 14, 0x7f800000, ERANGE); | ||
| runTest("1e-2147483647", 13, 0x0, ERANGE); | ||
| runTest("1e-2147483649", 13, 0x0, ERANGE); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, BasicHexadecimalTests) { | ||
| runTest("0x1", 3, 0x3f800000); | ||
| runTest("0x10", 4, 0x41800000); | ||
| runTest("0x11", 4, 0x41880000); | ||
| runTest("0x0.1234", 8, 0x3d91a000); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, HexadecimalSubnormalTests) { | ||
| runTest("0x0.0000000000000000000000000000000002", 38, 0x4000); | ||
|
|
||
| // This is the largest subnormal number as represented in hex | ||
| runTest("0x0.00000000000000000000000000000003fffff8", 42, 0x7fffff); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, HexadecimalSubnormalRoundingTests) { | ||
| // This is the largest subnormal number that gets rounded down to 0 (as a | ||
| // float) | ||
| runTest("0x0.00000000000000000000000000000000000004", 42, 0x0, ERANGE); | ||
|
|
||
| // This is slightly larger, and thus rounded up | ||
| runTest("0x0.000000000000000000000000000000000000041", 43, 0x00000001, | ||
| ERANGE); | ||
|
|
||
| // These check that we're rounding to even properly | ||
| runTest("0x0.0000000000000000000000000000000000000b", 42, 0x00000001, ERANGE); | ||
| runTest("0x0.0000000000000000000000000000000000000c", 42, 0x00000002, ERANGE); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, HexadecimalNormalRoundingTests) { | ||
| // This also checks the round to even behavior by checking three adjacent | ||
| // numbers. | ||
| // This gets rounded down to even | ||
| runTest("0x123456500", 11, 0x4f91a2b2); | ||
| // This doesn't get rounded at all | ||
| runTest("0x123456600", 11, 0x4f91a2b3); | ||
| // This gets rounded up to even | ||
| runTest("0x123456700", 11, 0x4f91a2b4); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, HexadecimalOutOfRangeTests) { | ||
| runTest("0x123456789123456789123456789123456789", 38, 0x7f800000, ERANGE); | ||
| runTest("-0x123456789123456789123456789123456789", 39, 0xff800000, ERANGE); | ||
| runTest("0x0.00000000000000000000000000000000000001", 42, 0x0, ERANGE); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, InfTests) { | ||
| runTest("INF", 3, 0x7f800000); | ||
| runTest("INFinity", 8, 0x7f800000); | ||
| runTest("infnity", 3, 0x7f800000); | ||
| runTest("infinit", 3, 0x7f800000); | ||
| runTest("infinfinit", 3, 0x7f800000); | ||
| runTest("innf", 0, 0x0); | ||
| runTest("-inf", 4, 0xff800000); | ||
| runTest("-iNfInItY", 9, 0xff800000); | ||
| } | ||
|
|
||
| TEST_F(LlvmLibcStrToFTest, NaNTests) { | ||
| runTest("NaN", 3, 0x7fc00000); | ||
| runTest("-nAn", 4, 0xffc00000); | ||
| runTest("NaN()", 5, 0x7fc00000); | ||
| runTest("NaN(1234)", 9, 0x7fc004d2); | ||
| runTest("NaN( 1234)", 3, 0x7fc00000); | ||
| } |