From 30a4e783a7618f17a5b24048625872e363068887 Mon Sep 17 00:00:00 2001 From: Simon Krueger Date: Sat, 2 Nov 2024 07:00:33 -0700 Subject: [PATCH] Add leading + sign support to str_to_floating_fast_float Summary: This adds support for a leading `+` sign (e.g., `+3.14`, `+.14`, `+123.456e10`). `folly::str_to_floating` supports a leading plus sign, but fast_float does not. Differential Revision: D65365934 fbshipit-source-id: fd9b622cb3a205d62f463a971efd28af9b722448 --- folly/Conv.cpp | 8 ++++++ folly/test/ConvTest.cpp | 61 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/folly/Conv.cpp b/folly/Conv.cpp index 28d43e78074..239f09914f7 100644 --- a/folly/Conv.cpp +++ b/folly/Conv.cpp @@ -359,6 +359,14 @@ Expected str_to_floating_fast_float_from_chars( return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING); } + if (*b == '+') { + // This function supports a leading + sign, but fast_float does not. + b += 1; + if (b == e || (!std::isdigit(*b) && *b != '.')) { + return makeUnexpected(ConversionCode::STRING_TO_FLOAT_ERROR); + } + } + Tgt result; auto [ptr, ec] = fast_float::from_chars(b, e, result); bool isOutOfRange{ec == std::errc::result_out_of_range}; diff --git a/folly/test/ConvTest.cpp b/folly/test/ConvTest.cpp index 56de7e2487e..43ca6601e03 100644 --- a/folly/test/ConvTest.cpp +++ b/folly/test/ConvTest.cpp @@ -1642,9 +1642,58 @@ template void tryStringToFloat(const StrToFloat& strToFloat) { auto rv1 = strToFloat(String("")); EXPECT_FALSE(rv1.hasValue()); + + const std::array kZero{{ + "0", + "+0", + "-0", + ".0", + "+.0", + "-.0", + "0.0", + "+0.0", + "-0.0", + }}; + for (const auto& input : kZero) { + auto rv = strToFloat(input); + EXPECT_TRUE(rv.hasValue()) << input; + EXPECT_EQ(rv.value(), 0.0f) << input; + } + auto rv2 = strToFloat(String("3.14")); EXPECT_TRUE(rv2.hasValue()); EXPECT_NEAR(rv2.value(), 3.14, 1e-5); + + auto rv2Positive = strToFloat(String("+3.14")); + EXPECT_TRUE(rv2Positive.hasValue()); + EXPECT_NEAR(rv2Positive.value(), 3.14, 1e-5); + + auto rv2PositiveLessThan1 = strToFloat(String("+.14")); + EXPECT_TRUE(rv2PositiveLessThan1.hasValue()); + EXPECT_NEAR(rv2PositiveLessThan1.value(), .14, 1e-5); + + const std::array kInvalidSigns{{ + "-", + "+", + "--3.14", + "++3.14", + "+-3.14", + "-+3.14", + }}; + for (const auto& input : kInvalidSigns) { + auto rv = strToFloat(input); + EXPECT_TRUE(rv.hasError()) << input; + EXPECT_EQ(rv.error(), ConversionCode::STRING_TO_FLOAT_ERROR) << input; + } + + auto rv2Negative = strToFloat(String("-3.14")); + EXPECT_TRUE(rv2Negative.hasValue()); + EXPECT_NEAR(rv2Negative.value(), -3.14, 1e-5); + + auto rv2NegativeLessThan1 = strToFloat(String("-.14")); + EXPECT_TRUE(rv2NegativeLessThan1.hasValue()); + EXPECT_NEAR(rv2NegativeLessThan1.value(), -.14, 1e-5); + // No trailing '\0' to expose 1-byte buffer over-read char x = '-'; auto rv3 = strToFloat(String(&x, 1)); @@ -1685,6 +1734,8 @@ void tryStringToFloat(const StrToFloat& strToFloat) { EXPECT_TRUE(std::isnan(rv.value())) << input; } + EXPECT_EQ(strToFloat("+nan").error(), ConversionCode::STRING_TO_FLOAT_ERROR); + const std::array kInfinityInputs{{ "-inf", "-INF", @@ -1706,18 +1757,26 @@ void tryStringToFloat(const StrToFloat& strToFloat) { } } - const std::array kScientificNotation{{ + EXPECT_EQ( + strToFloat("+infinity").error(), ConversionCode::STRING_TO_FLOAT_ERROR); + EXPECT_EQ(strToFloat("+inf").error(), ConversionCode::STRING_TO_FLOAT_ERROR); + + const std::array kScientificNotation{{ "123.4560e0", + "+123.4560e0", "123.4560e+0", "123.4560e-0", "123456.0e-3", "123456.0E-3", + "+123456.0E-3", "0.123456e3", "0.123456e+3", "0.123456E+3", + "+0.123456E+3", ".123456e3", ".123456e+3", ".123456E+3", + "+.123456E+3", }}; for (const auto& input : kScientificNotation) { auto rv = strToFloat(input);