600 changes: 368 additions & 232 deletions libc/src/__support/str_to_float.h

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions libc/src/stdio/scanf_core/converter_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,17 @@ LIBC_INLINE void write_float_with_length(char *str,
LengthModifier lm = to_conv.length_modifier;
switch (lm) {
case (LengthModifier::l): {
auto value = internal::strtofloatingpoint<double>(str, nullptr);
auto value = internal::strtofloatingpoint<double>(str);
*reinterpret_cast<double *>(output_ptr) = value;
break;
}
case (LengthModifier::L): {
auto value = internal::strtofloatingpoint<long double>(str, nullptr);
auto value = internal::strtofloatingpoint<long double>(str);
*reinterpret_cast<long double *>(output_ptr) = value;
break;
}
default: {
auto value = internal::strtofloatingpoint<float>(str, nullptr);
auto value = internal::strtofloatingpoint<float>(str);
*reinterpret_cast<float *>(output_ptr) = value;
break;
}
Expand Down
7 changes: 6 additions & 1 deletion libc/src/stdlib/atof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
#include "src/stdlib/atof.h"
#include "src/__support/common.h"
#include "src/__support/str_to_float.h"
#include <errno.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(double, atof, (const char *str)) {
return internal::strtofloatingpoint<double>(str, nullptr);
auto result = internal::strtofloatingpoint<double>(str);
if (result.has_error())
errno = result.error;

return result.value;
}

} // namespace __llvm_libc
10 changes: 9 additions & 1 deletion libc/src/stdlib/strtod.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@
#include "src/stdlib/strtod.h"
#include "src/__support/common.h"
#include "src/__support/str_to_float.h"
#include <errno.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(double, strtod,
(const char *__restrict str, char **__restrict str_end)) {
return internal::strtofloatingpoint<double>(str, str_end);
auto result = internal::strtofloatingpoint<double>(str);
if (result.has_error())
errno = result.error;

if (str_end != NULL)
*str_end = const_cast<char *>(str + result.parsed_len);

return result.value;
}

} // namespace __llvm_libc
10 changes: 9 additions & 1 deletion libc/src/stdlib/strtof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@
#include "src/stdlib/strtof.h"
#include "src/__support/common.h"
#include "src/__support/str_to_float.h"
#include <errno.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(float, strtof,
(const char *__restrict str, char **__restrict str_end)) {
return internal::strtofloatingpoint<float>(str, str_end);
auto result = internal::strtofloatingpoint<float>(str);
if (result.has_error())
errno = result.error;

if (str_end != NULL)
*str_end = const_cast<char *>(str + result.parsed_len);

return result.value;
}

} // namespace __llvm_libc
10 changes: 9 additions & 1 deletion libc/src/stdlib/strtold.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,20 @@
#include "src/stdlib/strtold.h"
#include "src/__support/common.h"
#include "src/__support/str_to_float.h"
#include <errno.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(long double, strtold,
(const char *__restrict str, char **__restrict str_end)) {
return internal::strtofloatingpoint<long double>(str, str_end);
auto result = internal::strtofloatingpoint<long double>(str);
if (result.has_error())
errno = result.error;

if (str_end != NULL)
*str_end = const_cast<char *>(str + result.parsed_len);

return result.value;
}

} // namespace __llvm_libc
99 changes: 54 additions & 45 deletions libc/test/src/__support/str_to_float_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ class LlvmLibcStrToFloatTest : public __llvm_libc::testing::Test {
0;
uint32_t actual_output_exp2 = 0;

ASSERT_TRUE(__llvm_libc::internal::clinger_fast_path<T>(
inputMantissa, inputExp10, &actual_output_mantissa,
&actual_output_exp2));
auto result = __llvm_libc::internal::clinger_fast_path<T>(
{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);
}
Expand All @@ -36,13 +41,9 @@ class LlvmLibcStrToFloatTest : public __llvm_libc::testing::Test {
void clinger_fast_path_fails_test(
const typename __llvm_libc::fputil::FPBits<T>::UIntType inputMantissa,
const int32_t inputExp10) {
typename __llvm_libc::fputil::FPBits<T>::UIntType actual_output_mantissa =
0;
uint32_t actual_output_exp2 = 0;

ASSERT_FALSE(__llvm_libc::internal::clinger_fast_path<T>(
inputMantissa, inputExp10, &actual_output_mantissa,
&actual_output_exp2));
ASSERT_FALSE(
__llvm_libc::internal::clinger_fast_path<T>({inputMantissa, inputExp10})
.has_value());
}

template <class T>
Expand All @@ -56,9 +57,14 @@ class LlvmLibcStrToFloatTest : public __llvm_libc::testing::Test {
0;
uint32_t actual_output_exp2 = 0;

ASSERT_TRUE(__llvm_libc::internal::eisel_lemire<T>(
inputMantissa, inputExp10, &actual_output_mantissa,
&actual_output_exp2));
auto result =
__llvm_libc::internal::eisel_lemire<T>({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);
}
Expand All @@ -74,11 +80,14 @@ class LlvmLibcStrToFloatTest : public __llvm_libc::testing::Test {
uint32_t actual_output_exp2 = 0;
errno = 0;

__llvm_libc::internal::simple_decimal_conversion<T>(
numStart, &actual_output_mantissa, &actual_output_exp2);
auto result = __llvm_libc::internal::simple_decimal_conversion<T>(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(errno, expectedErrno);
EXPECT_EQ(result.error, expectedErrno);
}
};

Expand Down Expand Up @@ -172,20 +181,17 @@ TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat64SpecificFailures) {
eisel_lemire_test<double>(2794967654709307188u, 1, 0x183e132bc608c9, 1087);
}

// Check the fallback states for the algorithm:
TEST_F(LlvmLibcStrToFloatTest, EiselLemireFallbackStates) {
// Check the fallback states for the algorithm:
uint32_t float_output_mantissa = 0;
uint64_t double_output_mantissa = 0;
uint32_t output_exp2 = 0;

// 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::eisel_lemire<double>(
12345678901234549760u, 0, &double_output_mantissa, &output_exp2));
ASSERT_FALSE(
__llvm_libc::internal::eisel_lemire<double>({12345678901234549760u, 0})
.has_value());

ASSERT_FALSE(__llvm_libc::internal::eisel_lemire<float>(
20040229, 0, &float_output_mantissa, &output_exp2));
ASSERT_FALSE(
__llvm_libc::internal::eisel_lemire<float>({20040229, 0}).has_value());
}

TEST_F(LlvmLibcStrToFloatTest, SimpleDecimalConversion64BasicWholeNumbers) {
Expand Down Expand Up @@ -245,21 +251,27 @@ TEST(LlvmLibcStrToFloatTest, SimpleDecimalConversionExtraTypes) {
uint32_t output_exp2 = 0;

errno = 0;
__llvm_libc::internal::simple_decimal_conversion<float>(
"123456789012345678900", &float_output_mantissa, &output_exp2);
auto float_result = __llvm_libc::internal::simple_decimal_conversion<float>(
"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(errno, 0);
EXPECT_EQ(float_result.error, 0);

uint64_t double_output_mantissa = 0;
output_exp2 = 0;

errno = 0;
__llvm_libc::internal::simple_decimal_conversion<double>(
"123456789012345678900", &double_output_mantissa, &output_exp2);
auto double_result = __llvm_libc::internal::simple_decimal_conversion<double>(
"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(errno, 0);
EXPECT_EQ(double_result.error, 0);
}

#if defined(LONG_DOUBLE_IS_DOUBLE)
Expand Down Expand Up @@ -299,20 +311,18 @@ TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat80TableLimits) {
}

TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat80Fallback) {
uint32_t outputExp2 = 0;
UInt128 quadOutputMantissa = 0;

// This number is halfway between two possible results, and the algorithm
// can't determine which is correct.
ASSERT_FALSE(__llvm_libc::internal::eisel_lemire<long double>(
12345678901234567890u, 1, &quadOutputMantissa, &outputExp2));
{12345678901234567890u, 1})
.has_value());

// These numbers' exponents are out of range for the current powers of ten
// table.
ASSERT_FALSE(__llvm_libc::internal::eisel_lemire<long double>(
1, 1000, &quadOutputMantissa, &outputExp2));
ASSERT_FALSE(__llvm_libc::internal::eisel_lemire<long double>(
1, -1000, &quadOutputMantissa, &outputExp2));
ASSERT_FALSE(
__llvm_libc::internal::eisel_lemire<long double>({1, 1000}).has_value());
ASSERT_FALSE(
__llvm_libc::internal::eisel_lemire<long double>({1, -1000}).has_value());
}
#else // Quad precision long double
TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat128Simple) {
Expand All @@ -336,11 +346,10 @@ TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat128LongerMantissa) {
}

TEST_F(LlvmLibcStrToFloatTest, EiselLemireFloat128Fallback) {
uint32_t outputExp2 = 0;
UInt128 quadOutputMantissa = 0;

ASSERT_FALSE(__llvm_libc::internal::eisel_lemire<long double>(
(UInt128(0x5ce0e9a56015fec5) << 64) + UInt128(0xaadfa328ae39b333), 1,
&quadOutputMantissa, &outputExp2));
ASSERT_FALSE(
__llvm_libc::internal::eisel_lemire<long double>(
{(UInt128(0x5ce0e9a56015fec5) << 64) + UInt128(0xaadfa328ae39b333),
1}, )
.has_value());
}
#endif
3 changes: 3 additions & 0 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -302,9 +302,12 @@ libc_support_library(
":__support_builtin_wrappers",
":__support_common",
":__support_cpp_limits",
":__support_cpp_optional",
":__support_ctype_utils",
":__support_fputil_fenv_impl",
":__support_fputil_fp_bits",
":__support_str_to_integer",
":__support_str_to_num_result",
":__support_uint128",
],
)
Expand Down