Commit 4c2692fa authored by Jun Mei's avatar Jun Mei Committed by Facebook GitHub Bot

Improve string to 32-bit float conversion near numeric limits

Summary: This change introduces a specialization for string-to-float (32-bit) conversion. It improves fidelity to the numeric value represented by the input string.

Reviewed By: vitaut

Differential Revision: D25239226

fbshipit-source-id: 531aa33aa74aaeeb98d9803bcd10bd7f31654988
parent 5a2072f6
......@@ -348,7 +348,7 @@ Expected<Tgt, ConversionCode> str_to_floating(StringPiece* src) noexcept {
StringToDoubleConverter::ALLOW_LEADING_SPACES,
0.0,
// return this for junk input string
std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<Tgt>::quiet_NaN(),
nullptr,
nullptr);
......@@ -356,11 +356,11 @@ Expected<Tgt, ConversionCode> str_to_floating(StringPiece* src) noexcept {
return makeUnexpected(ConversionCode::EMPTY_INPUT_STRING);
}
int length;
auto result = conv.StringToDouble(
src->data(),
static_cast<int>(src->size()),
&length); // processed char count
int length; // processed char count
auto result = std::is_same<Tgt, float>::value
? conv.StringToFloat(src->data(), static_cast<int>(src->size()), &length)
: static_cast<Tgt>(conv.StringToDouble(
src->data(), static_cast<int>(src->size()), &length));
if (!std::isnan(result)) {
// If we get here with length = 0, the input string is empty.
......
......@@ -902,6 +902,25 @@ TEST(Conv, FloatToBool) {
EXPECT_EQ(to<bool>(-std::numeric_limits<double>::infinity()), true);
}
TEST(Conv, RoundTripFloatToStringToFloat) {
const std::array<float, 6> kTests{{
3.14159f,
12345678.f,
numeric_limits<float>::lowest(),
numeric_limits<float>::max(),
numeric_limits<float>::infinity(),
-numeric_limits<float>::infinity(),
}};
for (const auto& test : kTests) {
SCOPED_TRACE(to<string>(test));
EXPECT_EQ(to<float>(to<string>(test)), test);
}
EXPECT_TRUE(
std::isnan(to<float>(to<string>(numeric_limits<float>::quiet_NaN()))));
}
namespace {
template <typename F>
......@@ -972,6 +991,13 @@ TEST(Conv, ConversionErrorStrToFloat) {
EXPECT_CONV_ERROR_STR(float, "\t", EMPTY_INPUT_STRING);
EXPECT_CONV_ERROR_STR(float, " junk", STRING_TO_FLOAT_ERROR);
EXPECT_CONV_ERROR(to<float>(" 1bla"), NON_WHITESPACE_AFTER_END, "bla");
EXPECT_CONV_ERROR_STR_NOVAL(double, StringPiece(), EMPTY_INPUT_STRING);
EXPECT_CONV_ERROR_STR_NOVAL(double, "", EMPTY_INPUT_STRING);
EXPECT_CONV_ERROR_STR(double, " ", EMPTY_INPUT_STRING);
EXPECT_CONV_ERROR_STR(double, "\t", EMPTY_INPUT_STRING);
EXPECT_CONV_ERROR_STR(double, " junk", STRING_TO_FLOAT_ERROR);
EXPECT_CONV_ERROR(to<double>(" 1bla"), NON_WHITESPACE_AFTER_END, "bla");
}
TEST(Conv, ConversionErrorStrToInt) {
......@@ -1206,6 +1232,41 @@ TEST(Conv, TryStringToFloat) {
char x = '-';
auto rv3 = folly::tryTo<float>(folly::StringPiece(&x, 1));
EXPECT_FALSE(rv3.hasValue());
// Exact conversion at numeric limits (8+ decimal digits)
auto rv4 = folly::tryTo<float>("-3.4028235E38");
EXPECT_TRUE(rv4.hasValue());
EXPECT_EQ(rv4.value(), numeric_limits<float>::lowest());
auto rv5 = folly::tryTo<float>("3.40282346E38");
EXPECT_TRUE(rv5.hasValue());
EXPECT_EQ(rv5.value(), numeric_limits<float>::max());
// Beyond numeric limits
// numeric_limits<float>::lowest() ~= -3.402823466E38
const std::array<folly::StringPiece, 4> kOversizedInputs{{
"-3.403E38",
"-3.4029E38",
"-3.402824E38",
"-3.4028236E38",
}};
for (const auto& input : kOversizedInputs) {
auto rv = folly::tryTo<float>(input);
EXPECT_EQ(rv.value(), -numeric_limits<float>::infinity()) << input;
}
// NaN
const std::array<folly::StringPiece, 6> kNanInputs{{
"nan",
"NaN",
"NAN",
"-nan",
"-NaN",
"-NAN",
}};
for (const auto& input : kNanInputs) {
auto rv = folly::tryTo<float>(input);
EXPECT_TRUE(std::isnan(rv.value())) << input;
}
}
TEST(Conv, TryStringToDouble) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment