diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt index 740209bc83d75..fe8b3c4c84c38 100644 --- a/libc/test/src/__support/CMakeLists.txt +++ b/libc/test/src/__support/CMakeLists.txt @@ -50,6 +50,8 @@ add_libc_test( libc-support-tests SRCS str_to_float_test.cpp + str_to_double_test.cpp + str_to_long_double_test.cpp DEPENDS libc.src.__support.str_to_float libc.src.__support.uint128 diff --git a/libc/test/src/__support/str_to_double_test.cpp b/libc/test/src/__support/str_to_double_test.cpp new file mode 100644 index 0000000000000..b66935f0988e0 --- /dev/null +++ b/libc/test/src/__support/str_to_double_test.cpp @@ -0,0 +1,105 @@ +#include "str_to_fp_test.h" + +namespace LIBC_NAMESPACE { +using LlvmLibcStrToDblTest = LlvmLibcStrToFloatTest; + +TEST_F(LlvmLibcStrToDblTest, ClingerFastPathFloat64Simple) { + clinger_fast_path_test(123, 0, 0xEC00000000000, 1029); + clinger_fast_path_test(1234567890123456, 1, 0x5ee2a2eb5a5c0, 1076); + clinger_fast_path_test(1234567890, -10, 0xf9add3739635f, 1019); +} + +TEST_F(LlvmLibcStrToDblTest, ClingerFastPathFloat64ExtendedExp) { + clinger_fast_path_test(1, 30, 0x93e5939a08cea, 1122); + clinger_fast_path_test(1, 37, 0xe17b84357691b, 1145); + clinger_fast_path_fails_test(10, 37); + clinger_fast_path_fails_test(1, 100); +} + +TEST_F(LlvmLibcStrToDblTest, ClingerFastPathFloat64NegativeExp) { + clinger_fast_path_test(1, -10, 0xb7cdfd9d7bdbb, 989); + clinger_fast_path_test(1, -20, 0x79ca10c924223, 956); + clinger_fast_path_fails_test(1, -25); +} + +TEST_F(LlvmLibcStrToDblTest, EiselLemireFloat64Simple) { + eisel_lemire_test(12345678901234567890u, 1, 0x1AC53A7E04BCDA, 1089); + eisel_lemire_test(123, 0, 0x1EC00000000000, 1029); + eisel_lemire_test(12345678901234568192u, 0, 0x156A95319D63E2, 1086); +} + +TEST_F(LlvmLibcStrToDblTest, EiselLemireFloat64SpecificFailures) { + // These test cases have caused failures in the past. + eisel_lemire_test(358416272, -33, 0x1BBB2A68C9D0B9, 941); + eisel_lemire_test(2166568064000000238u, -9, 0x10246690000000, 1054); + eisel_lemire_test(2794967654709307187u, 1, 0x183e132bc608c8, 1087); + eisel_lemire_test(2794967654709307188u, 1, 0x183e132bc608c9, 1087); +} + +// Check the fallback states for the algorithm: +TEST_F(LlvmLibcStrToDblTest, EiselLemireFallbackStates) { + // 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( + internal::eisel_lemire({12345678901234549760u, 0}).has_value()); +} + +TEST_F(LlvmLibcStrToDblTest, SimpleDecimalConversion64BasicWholeNumbers) { + simple_decimal_conversion_test("123456789012345678900", 0x1AC53A7E04BCDA, + 1089); + simple_decimal_conversion_test("123", 0x1EC00000000000, 1029); + simple_decimal_conversion_test("12345678901234549760", 0x156A95319D63D8, + 1086); +} + +TEST_F(LlvmLibcStrToDblTest, SimpleDecimalConversion64BasicDecimals) { + simple_decimal_conversion_test("1.2345", 0x13c083126e978d, 1023); + simple_decimal_conversion_test(".2345", 0x1e04189374bc6a, 1020); + simple_decimal_conversion_test(".299792458", 0x132fccb4aca314, 1021); +} + +TEST_F(LlvmLibcStrToDblTest, SimpleDecimalConversion64BasicExponents) { + simple_decimal_conversion_test("1e10", 0x12a05f20000000, 1056); + simple_decimal_conversion_test("1e-10", 0x1b7cdfd9d7bdbb, 989); + simple_decimal_conversion_test("1e300", 0x17e43c8800759c, 2019); + simple_decimal_conversion_test("1e-300", 0x156e1fc2f8f359, 26); +} + +TEST_F(LlvmLibcStrToDblTest, SimpleDecimalConversion64BasicSubnormals) { + simple_decimal_conversion_test("1e-320", 0x7e8, 0, ERANGE); + simple_decimal_conversion_test("1e-308", 0x730d67819e8d2, 0, ERANGE); + simple_decimal_conversion_test("2.9e-308", 0x14da6df5e4bcc8, 1); +} + +TEST_F(LlvmLibcStrToDblTest, 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. + simple_decimal_conversion_test("2.225073858507201e-308", 0xfffffffffffff, 0, + ERANGE); + + // 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. + simple_decimal_conversion_test("2.2250738585072012e-308", 0x10000000000000, + 1); +} + +TEST(LlvmLibcStrToDblTest, SimpleDecimalConversionExtraTypes) { + uint64_t double_output_mantissa = 0; + uint32_t output_exp2 = 0; + + libc_errno = 0; + auto double_result = + internal::simple_decimal_conversion("123456789012345678900"); + + double_output_mantissa = double_result.num.mantissa; + output_exp2 = double_result.num.exponent; + + EXPECT_EQ(double_output_mantissa, uint64_t(0x1AC53A7E04BCDA)); + EXPECT_EQ(output_exp2, uint32_t(1089)); + EXPECT_EQ(double_result.error, 0); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/str_to_float_test.cpp b/libc/test/src/__support/str_to_float_test.cpp index 35f7318fb9c78..3102fa7aa91ec 100644 --- a/libc/test/src/__support/str_to_float_test.cpp +++ b/libc/test/src/__support/str_to_float_test.cpp @@ -1,321 +1,59 @@ -//===-- 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 "str_to_fp_test.h" -#include "src/__support/FPUtil/FPBits.h" -#include "src/__support/UInt128.h" -#include "src/__support/str_to_float.h" -#include "src/errno/libc_errno.h" +namespace LIBC_NAMESPACE { -#include "test/UnitTest/Test.h" +using LlvmLibcStrToFltTest = LlvmLibcStrToFloatTest; -class LlvmLibcStrToFloatTest : public LIBC_NAMESPACE::testing::Test { -public: - template - void clinger_fast_path_test( - const typename LIBC_NAMESPACE::fputil::FPBits::UIntType inputMantissa, - const int32_t inputExp10, - const typename LIBC_NAMESPACE::fputil::FPBits::UIntType - expectedOutputMantissa, - const uint32_t expectedOutputExp2) { - typename LIBC_NAMESPACE::fputil::FPBits::UIntType - actual_output_mantissa = 0; - uint32_t actual_output_exp2 = 0; - - auto result = LIBC_NAMESPACE::internal::clinger_fast_path( - {inputMantissa, inputExp10}); - - ASSERT_TRUE(result.has_value()); - - actual_output_mantissa = result->mantissa; - actual_output_exp2 = result->exponent; - - EXPECT_EQ(actual_output_mantissa, expectedOutputMantissa); - EXPECT_EQ(actual_output_exp2, expectedOutputExp2); - } - - template - void clinger_fast_path_fails_test( - const typename LIBC_NAMESPACE::fputil::FPBits::UIntType inputMantissa, - const int32_t inputExp10) { - ASSERT_FALSE(LIBC_NAMESPACE::internal::clinger_fast_path( - {inputMantissa, inputExp10}) - .has_value()); - } - - template - void eisel_lemire_test( - const typename LIBC_NAMESPACE::fputil::FPBits::UIntType inputMantissa, - const int32_t inputExp10, - const typename LIBC_NAMESPACE::fputil::FPBits::UIntType - expectedOutputMantissa, - const uint32_t expectedOutputExp2) { - typename LIBC_NAMESPACE::fputil::FPBits::UIntType - actual_output_mantissa = 0; - uint32_t actual_output_exp2 = 0; - - auto result = - LIBC_NAMESPACE::internal::eisel_lemire({inputMantissa, inputExp10}); - - ASSERT_TRUE(result.has_value()); - - actual_output_mantissa = result->mantissa; - actual_output_exp2 = result->exponent; - - EXPECT_EQ(actual_output_mantissa, expectedOutputMantissa); - EXPECT_EQ(actual_output_exp2, expectedOutputExp2); - } - - template - void simple_decimal_conversion_test( - const char *__restrict numStart, - const typename LIBC_NAMESPACE::fputil::FPBits::UIntType - expectedOutputMantissa, - const uint32_t expectedOutputExp2, const int expectedErrno = 0) { - typename LIBC_NAMESPACE::fputil::FPBits::UIntType - actual_output_mantissa = 0; - uint32_t actual_output_exp2 = 0; - libc_errno = 0; - - auto result = - LIBC_NAMESPACE::internal::simple_decimal_conversion(numStart); - - actual_output_mantissa = result.num.mantissa; - actual_output_exp2 = result.num.exponent; - - EXPECT_EQ(actual_output_mantissa, expectedOutputMantissa); - EXPECT_EQ(actual_output_exp2, expectedOutputExp2); - EXPECT_EQ(result.error, expectedErrno); - } -}; - -TEST_F(LlvmLibcStrToFloatTest, ClingerFastPathFloat64Simple) { - clinger_fast_path_test(123, 0, 0xEC00000000000, 1029); - clinger_fast_path_test(1234567890123456, 1, 0x5ee2a2eb5a5c0, 1076); - clinger_fast_path_test(1234567890, -10, 0xf9add3739635f, 1019); +TEST_F(LlvmLibcStrToFltTest, ClingerFastPathFloat32Simple) { + clinger_fast_path_test(123, 0, 0x760000, 133); + clinger_fast_path_test(1234567, 1, 0x3c6146, 150); + clinger_fast_path_test(12345, -5, 0x7cd35b, 123); } -TEST_F(LlvmLibcStrToFloatTest, ClingerFastPathFloat64ExtendedExp) { - clinger_fast_path_test(1, 30, 0x93e5939a08cea, 1122); - clinger_fast_path_test(1, 37, 0xe17b84357691b, 1145); - clinger_fast_path_fails_test(10, 37); - clinger_fast_path_fails_test(1, 100); +TEST_F(LlvmLibcStrToFltTest, ClingerFastPathFloat32ExtendedExp) { + clinger_fast_path_test(1, 15, 0x635fa9, 176); + clinger_fast_path_test(1, 17, 0x31a2bc, 183); + clinger_fast_path_fails_test(10, 17); + clinger_fast_path_fails_test(1, 50); } -TEST_F(LlvmLibcStrToFloatTest, ClingerFastPathFloat64NegativeExp) { - clinger_fast_path_test(1, -10, 0xb7cdfd9d7bdbb, 989); - clinger_fast_path_test(1, -20, 0x79ca10c924223, 956); - clinger_fast_path_fails_test(1, -25); -} - -TEST_F(LlvmLibcStrToFloatTest, ClingerFastPathFloat32Simple) { - clinger_fast_path_test(123, 0, 0x760000, 133); - clinger_fast_path_test(1234567, 1, 0x3c6146, 150); - clinger_fast_path_test(12345, -5, 0x7cd35b, 123); -} - -TEST_F(LlvmLibcStrToFloatTest, ClingerFastPathFloat32ExtendedExp) { - clinger_fast_path_test(1, 15, 0x635fa9, 176); - clinger_fast_path_test(1, 17, 0x31a2bc, 183); - clinger_fast_path_fails_test(10, 17); - clinger_fast_path_fails_test(1, 50); -} - -TEST_F(LlvmLibcStrToFloatTest, ClingerFastPathFloat32NegativeExp) { - clinger_fast_path_test(1, -5, 0x27c5ac, 110); - clinger_fast_path_test(1, -10, 0x5be6ff, 93); - clinger_fast_path_fails_test(1, -15); -} - -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat64Simple) { - eisel_lemire_test(12345678901234567890u, 1, 0x1AC53A7E04BCDA, 1089); - eisel_lemire_test(123, 0, 0x1EC00000000000, 1029); - eisel_lemire_test(12345678901234568192u, 0, 0x156A95319D63E2, 1086); -} - -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat64SpecificFailures) { - // These test cases have caused failures in the past. - eisel_lemire_test(358416272, -33, 0x1BBB2A68C9D0B9, 941); - eisel_lemire_test(2166568064000000238u, -9, 0x10246690000000, 1054); - eisel_lemire_test(2794967654709307187u, 1, 0x183e132bc608c8, 1087); - eisel_lemire_test(2794967654709307188u, 1, 0x183e132bc608c9, 1087); +TEST_F(LlvmLibcStrToFltTest, ClingerFastPathFloat32NegativeExp) { + clinger_fast_path_test(1, -5, 0x27c5ac, 110); + clinger_fast_path_test(1, -10, 0x5be6ff, 93); + clinger_fast_path_fails_test(1, -15); } // Check the fallback states for the algorithm: -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFallbackStates) { +TEST_F(LlvmLibcStrToFltTest, EiselLemireFallbackStates) { // 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( - LIBC_NAMESPACE::internal::eisel_lemire({12345678901234549760u, 0}) - .has_value()); - - ASSERT_FALSE( - LIBC_NAMESPACE::internal::eisel_lemire({20040229, 0}).has_value()); + ASSERT_FALSE(internal::eisel_lemire({20040229, 0}).has_value()); } -TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicWholeNumbers) { - simple_decimal_conversion_test("123456789012345678900", - 0x1AC53A7E04BCDA, 1089); - simple_decimal_conversion_test("123", 0x1EC00000000000, 1029); - simple_decimal_conversion_test("12345678901234549760", - 0x156A95319D63D8, 1086); -} - -TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicDecimals) { - simple_decimal_conversion_test("1.2345", 0x13c083126e978d, 1023); - simple_decimal_conversion_test(".2345", 0x1e04189374bc6a, 1020); - simple_decimal_conversion_test(".299792458", 0x132fccb4aca314, 1021); -} - -TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicExponents) { - simple_decimal_conversion_test("1e10", 0x12a05f20000000, 1056); - simple_decimal_conversion_test("1e-10", 0x1b7cdfd9d7bdbb, 989); - simple_decimal_conversion_test("1e300", 0x17e43c8800759c, 2019); - simple_decimal_conversion_test("1e-300", 0x156e1fc2f8f359, 26); -} - -TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicSubnormals) { - simple_decimal_conversion_test("1e-320", 0x7e8, 0, ERANGE); - simple_decimal_conversion_test("1e-308", 0x730d67819e8d2, 0, ERANGE); - simple_decimal_conversion_test("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. - simple_decimal_conversion_test("2.225073858507201e-308", - 0xfffffffffffff, 0, ERANGE); - - // 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. - simple_decimal_conversion_test("2.2250738585072012e-308", - 0x10000000000000, 1); -} - -TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion32SpecificFailures) { - simple_decimal_conversion_test( +TEST_F(LlvmLibcStrToFltTest, SimpleDecimalConversion32SpecificFailures) { + simple_decimal_conversion_test( "1.4012984643248170709237295832899161312802619418765e-45", 0x1, 0, ERANGE); - simple_decimal_conversion_test( + simple_decimal_conversion_test( "7." "006492321624085354618647916449580656401309709382578858785341419448955413" "42930300743319094181060791015625e-46", 0x0, 0, ERANGE); } -TEST(LlvmLibcStrToFloatTest, SimpleDecimalConversionExtraTypes) { +TEST(LlvmLibcStrToFltTest, SimpleDecimalConversionExtraTypes) { uint32_t float_output_mantissa = 0; uint32_t output_exp2 = 0; libc_errno = 0; auto float_result = - LIBC_NAMESPACE::internal::simple_decimal_conversion( - "123456789012345678900"); + internal::simple_decimal_conversion("123456789012345678900"); float_output_mantissa = float_result.num.mantissa; output_exp2 = float_result.num.exponent; EXPECT_EQ(float_output_mantissa, uint32_t(0xd629d4)); EXPECT_EQ(output_exp2, uint32_t(193)); EXPECT_EQ(float_result.error, 0); - - uint64_t double_output_mantissa = 0; - output_exp2 = 0; - - libc_errno = 0; - auto double_result = - LIBC_NAMESPACE::internal::simple_decimal_conversion( - "123456789012345678900"); - - double_output_mantissa = double_result.num.mantissa; - output_exp2 = double_result.num.exponent; - - EXPECT_EQ(double_output_mantissa, uint64_t(0x1AC53A7E04BCDA)); - EXPECT_EQ(output_exp2, uint32_t(1089)); - EXPECT_EQ(double_result.error, 0); } -#if defined(LIBC_LONG_DOUBLE_IS_FLOAT64) -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat64AsLongDouble) { - eisel_lemire_test(123, 0, 0x1EC00000000000, 1029); -} -#elif defined(LIBC_LONG_DOUBLE_IS_X86_FLOAT80) -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat80Simple) { - eisel_lemire_test(123, 0, 0xf600000000000000, 16389); - eisel_lemire_test(12345678901234568192u, 0, 0xab54a98ceb1f0c00, - 16446); -} - -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat80LongerMantissa) { - eisel_lemire_test((UInt128(0x1234567812345678) << 64) + - UInt128(0x1234567812345678), - 0, 0x91a2b3c091a2b3c1, 16507); - eisel_lemire_test((UInt128(0x1234567812345678) << 64) + - UInt128(0x1234567812345678), - 300, 0xd97757de56adb65c, 17503); - eisel_lemire_test((UInt128(0x1234567812345678) << 64) + - UInt128(0x1234567812345678), - -300, 0xc30feb9a7618457d, 15510); -} - -// These tests check numbers at the edge of the DETAILED_POWERS_OF_TEN table. -// This doesn't reach very far into the range for long doubles, since it's sized -// for doubles and their 11 exponent bits, and not for long doubles and their -// 15 exponent bits. This is a known tradeoff, and was made because a proper -// long double table would be approximately 16 times longer (specifically the -// maximum exponent would need to be about 5000, leading to a 10,000 entry -// table). This would have significant memory and storage costs all the time to -// speed up a relatively uncommon path. -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat80TableLimits) { - eisel_lemire_test(1, 347, 0xd13eb46469447567, 17535); - eisel_lemire_test(1, -348, 0xfa8fd5a0081c0288, 15226); -} - -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat80Fallback) { - // This number is halfway between two possible results, and the algorithm - // can't determine which is correct. - ASSERT_FALSE(LIBC_NAMESPACE::internal::eisel_lemire( - {12345678901234567890u, 1}) - .has_value()); - - // These numbers' exponents are out of range for the current powers of ten - // table. - ASSERT_FALSE(LIBC_NAMESPACE::internal::eisel_lemire({1, 1000}) - .has_value()); - ASSERT_FALSE(LIBC_NAMESPACE::internal::eisel_lemire({1, -1000}) - .has_value()); -} -#else // Quad precision long double -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat128Simple) { - eisel_lemire_test(123, 0, (UInt128(0x1ec0000000000) << 64), - 16389); - eisel_lemire_test( - 12345678901234568192u, 0, - (UInt128(0x156a95319d63e) << 64) + UInt128(0x1800000000000000), 16446); -} - -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat128LongerMantissa) { - eisel_lemire_test( - (UInt128(0x1234567812345678) << 64) + UInt128(0x1234567812345678), 0, - (UInt128(0x1234567812345) << 64) + UInt128(0x6781234567812345), 16507); - eisel_lemire_test( - (UInt128(0x1234567812345678) << 64) + UInt128(0x1234567812345678), 300, - (UInt128(0x1b2eeafbcad5b) << 64) + UInt128(0x6cb8b4451dfcde19), 17503); - eisel_lemire_test( - (UInt128(0x1234567812345678) << 64) + UInt128(0x1234567812345678), -300, - (UInt128(0x1861fd734ec30) << 64) + UInt128(0x8afa7189f0f7595f), 15510); -} - -TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat128Fallback) { - ASSERT_FALSE( - LIBC_NAMESPACE::internal::eisel_lemire( - {(UInt128(0x5ce0e9a56015fec5) << 64) + UInt128(0xaadfa328ae39b333), - 1}) - .has_value()); -} -#endif +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/str_to_fp_test.h b/libc/test/src/__support/str_to_fp_test.h new file mode 100644 index 0000000000000..3b891fcddd3c6 --- /dev/null +++ b/libc/test/src/__support/str_to_fp_test.h @@ -0,0 +1,81 @@ +//===-- 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/FloatProperties.h" +#include "src/__support/UInt128.h" +#include "src/__support/str_to_float.h" +#include "src/errno/libc_errno.h" + +#include "test/UnitTest/Test.h" + +namespace LIBC_NAMESPACE { + +template struct LlvmLibcStrToFloatTest : public testing::Test { + using UIntType = typename fputil::FloatProperties::UIntType; + + void clinger_fast_path_test(const UIntType inputMantissa, + const int32_t inputExp10, + const UIntType expectedOutputMantissa, + const uint32_t expectedOutputExp2) { + UIntType actual_output_mantissa = 0; + uint32_t actual_output_exp2 = 0; + + auto result = internal::clinger_fast_path({inputMantissa, inputExp10}); + + ASSERT_TRUE(result.has_value()); + + actual_output_mantissa = result->mantissa; + actual_output_exp2 = result->exponent; + + EXPECT_EQ(actual_output_mantissa, expectedOutputMantissa); + EXPECT_EQ(actual_output_exp2, expectedOutputExp2); + } + + void clinger_fast_path_fails_test(const UIntType inputMantissa, + const int32_t inputExp10) { + ASSERT_FALSE(internal::clinger_fast_path({inputMantissa, inputExp10}) + .has_value()); + } + + void eisel_lemire_test(const UIntType inputMantissa, const int32_t inputExp10, + const UIntType expectedOutputMantissa, + const uint32_t expectedOutputExp2) { + UIntType actual_output_mantissa = 0; + uint32_t actual_output_exp2 = 0; + + auto result = internal::eisel_lemire({inputMantissa, inputExp10}); + + ASSERT_TRUE(result.has_value()); + + actual_output_mantissa = result->mantissa; + actual_output_exp2 = result->exponent; + + EXPECT_EQ(actual_output_mantissa, expectedOutputMantissa); + EXPECT_EQ(actual_output_exp2, expectedOutputExp2); + } + + void simple_decimal_conversion_test(const char *__restrict numStart, + const UIntType expectedOutputMantissa, + const uint32_t expectedOutputExp2, + const int expectedErrno = 0) { + UIntType actual_output_mantissa = 0; + uint32_t actual_output_exp2 = 0; + libc_errno = 0; + + auto result = internal::simple_decimal_conversion(numStart); + + actual_output_mantissa = result.num.mantissa; + actual_output_exp2 = result.num.exponent; + + EXPECT_EQ(actual_output_mantissa, expectedOutputMantissa); + EXPECT_EQ(actual_output_exp2, expectedOutputExp2); + EXPECT_EQ(result.error, expectedErrno); + } +}; + +} // namespace LIBC_NAMESPACE diff --git a/libc/test/src/__support/str_to_long_double_test.cpp b/libc/test/src/__support/str_to_long_double_test.cpp new file mode 100644 index 0000000000000..6fefc89ac3a94 --- /dev/null +++ b/libc/test/src/__support/str_to_long_double_test.cpp @@ -0,0 +1,88 @@ +#include "str_to_fp_test.h" + +namespace LIBC_NAMESPACE { + +using LlvmLibcStrToLongDblTest = LlvmLibcStrToFloatTest; + +#if defined(LIBC_LONG_DOUBLE_IS_FLOAT64) + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat64AsLongDouble) { + eisel_lemire_test(123, 0, 0x1EC00000000000, 1029); +} + +#elif defined(LIBC_LONG_DOUBLE_IS_X86_FLOAT80) + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat80Simple) { + eisel_lemire_test(123, 0, 0xf600000000000000, 16389); + eisel_lemire_test(12345678901234568192u, 0, 0xab54a98ceb1f0c00, 16446); +} + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat80LongerMantissa) { + eisel_lemire_test((UInt128(0x1234567812345678) << 64) + + UInt128(0x1234567812345678), + 0, 0x91a2b3c091a2b3c1, 16507); + eisel_lemire_test((UInt128(0x1234567812345678) << 64) + + UInt128(0x1234567812345678), + 300, 0xd97757de56adb65c, 17503); + eisel_lemire_test((UInt128(0x1234567812345678) << 64) + + UInt128(0x1234567812345678), + -300, 0xc30feb9a7618457d, 15510); +} + +// These tests check numbers at the edge of the DETAILED_POWERS_OF_TEN table. +// This doesn't reach very far into the range for long doubles, since it's sized +// for doubles and their 11 exponent bits, and not for long doubles and their +// 15 exponent bits. This is a known tradeoff, and was made because a proper +// long double table would be approximately 16 times longer (specifically the +// maximum exponent would need to be about 5000, leading to a 10,000 entry +// table). This would have significant memory and storage costs all the time to +// speed up a relatively uncommon path. +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat80TableLimits) { + eisel_lemire_test(1, 347, 0xd13eb46469447567, 17535); + eisel_lemire_test(1, -348, 0xfa8fd5a0081c0288, 15226); +} + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat80Fallback) { + // This number is halfway between two possible results, and the algorithm + // can't determine which is correct. + ASSERT_FALSE(internal::eisel_lemire({12345678901234567890u, 1}) + .has_value()); + + // These numbers' exponents are out of range for the current powers of ten + // table. + ASSERT_FALSE(internal::eisel_lemire({1, 1000}).has_value()); + ASSERT_FALSE(internal::eisel_lemire({1, -1000}).has_value()); +} + +#else // Quad precision long double + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat128Simple) { + eisel_lemire_test(123, 0, (UInt128(0x1ec0000000000) << 64), 16389); + eisel_lemire_test( + 12345678901234568192u, 0, + (UInt128(0x156a95319d63e) << 64) + UInt128(0x1800000000000000), 16446); +} + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat128LongerMantissa) { + eisel_lemire_test( + (UInt128(0x1234567812345678) << 64) + UInt128(0x1234567812345678), 0, + (UInt128(0x1234567812345) << 64) + UInt128(0x6781234567812345), 16507); + eisel_lemire_test( + (UInt128(0x1234567812345678) << 64) + UInt128(0x1234567812345678), 300, + (UInt128(0x1b2eeafbcad5b) << 64) + UInt128(0x6cb8b4451dfcde19), 17503); + eisel_lemire_test( + (UInt128(0x1234567812345678) << 64) + UInt128(0x1234567812345678), -300, + (UInt128(0x1861fd734ec30) << 64) + UInt128(0x8afa7189f0f7595f), 15510); +} + +TEST_F(LlvmLibcStrToLongDblTest, EiselLemireFloat128Fallback) { + ASSERT_FALSE( + internal::eisel_lemire( + {(UInt128(0x5ce0e9a56015fec5) << 64) + UInt128(0xaadfa328ae39b333), + 1}) + .has_value()); +} + +#endif + +} // namespace LIBC_NAMESPACE diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel index a973e6541da01..11a7b3b7404ff 100644 --- a/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/test/src/__support/BUILD.bazel @@ -42,9 +42,14 @@ libc_test( libc_test( name = "str_to_float_test", - srcs = ["str_to_float_test.cpp"], + srcs = [ + "str_to_double_test.cpp", + "str_to_float_test.cpp", + "str_to_fp_test.h", + "str_to_long_double_test.cpp", + ], deps = [ - "//libc:__support_fputil_fp_bits", + "//libc:__support_fputil_float_properties", "//libc:__support_str_to_float", "//libc:__support_uint128", ],