Commit 7f9c536c by Abseil Team Committed by Copybara-Service

Implement Eisel-Lemire for from_chars<float>

This does for float what a recent commit did for double.

Median of 5 runs of "time atod_manual_test pnftd/data/*.txt"
user    0m0.730s  # Before
user    0m0.701s  # After (a speed-up of 1.04x)
where pnftd is https://github.com/nigeltao/parse-number-fxx-test-data

Part of the reason why this speed-up of 1.04x isn't as dramatic as for
the from_chars<double> change is that, out of the 5299993 pnftd test
cases, 76.42% produce result_out_of_range for single precision (compared
to 1.03% for double precision).

"benchy --reference=srcfs --benchmark_filter='SimpleAtof' :numbers_benchmark"
output (which uses deterministic but randomly generated input strings):
name                                    old cpu/op  new cpu/op  delta
BM_SimpleAtof<absl::string_view>/10/1   392ns ± 2%   323ns ± 3%  -17.60%  (p=0.000 n=48+48)
BM_SimpleAtof<absl::string_view>/10/2   426ns ± 3%   311ns ± 4%  -26.89%  (p=0.000 n=59+49)
BM_SimpleAtof<absl::string_view>/10/4   435ns ± 3%   341ns ± 3%  -21.68%  (p=0.000 n=58+48)
BM_SimpleAtof<absl::string_view>/10/8   501ns ± 3%   393ns ± 3%  -21.55%  (p=0.000 n=60+50)
BM_SimpleAtof<const char*>/10/1         409ns ± 6%   339ns ± 3%  -17.06%  (p=0.000 n=48+49)
BM_SimpleAtof<const char*>/10/2         412ns ± 4%   347ns ± 3%  -15.82%  (p=0.000 n=47+49)
BM_SimpleAtof<const char*>/10/4         463ns ± 6%   369ns ± 6%  -20.37%  (p=0.000 n=60+50)
BM_SimpleAtof<const char*>/10/8         548ns ± 3%   450ns ± 4%  -17.91%  (p=0.000 n=57+59)
BM_SimpleAtof<std::string>/10/1         386ns ± 2%   325ns ± 3%  -15.74%  (p=0.000 n=48+50)
BM_SimpleAtof<std::string>/10/2         425ns ± 3%   311ns ± 4%  -26.79%  (p=0.000 n=60+50)
BM_SimpleAtof<std::string>/10/4         435ns ± 4%   340ns ± 3%  -21.94%  (p=0.000 n=59+49)
BM_SimpleAtof<std::string>/10/8         503ns ± 4%   398ns ± 2%  -20.89%  (p=0.000 n=59+48)

PiperOrigin-RevId: 476880111
Change-Id: Ibc5583677ac2ed338d09d8db960ae8a513eb2ccb
parent 8b951b09
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <cfenv> // NOLINT(build/c++11) #include <cfenv> // NOLINT(build/c++11)
#include <cfloat>
#include <cinttypes> #include <cinttypes>
#include <climits> #include <climits>
#include <cmath> #include <cmath>
...@@ -388,7 +389,18 @@ TEST(NumbersTest, Atoi) { ...@@ -388,7 +389,18 @@ TEST(NumbersTest, Atoi) {
} }
TEST(NumbersTest, Atod) { TEST(NumbersTest, Atod) {
// DBL_TRUE_MIN and FLT_TRUE_MIN were not mandated in <cfloat> before C++17.
#if !defined(DBL_TRUE_MIN)
static constexpr double DBL_TRUE_MIN =
4.940656458412465441765687928682213723650598026143247644255856825e-324;
#endif
#if !defined(FLT_TRUE_MIN)
static constexpr float FLT_TRUE_MIN =
1.401298464324817070923729583289916131280261941876515771757068284e-45f;
#endif
double d; double d;
float f;
// NaN can be spelled in multiple ways. // NaN can be spelled in multiple ways.
EXPECT_TRUE(absl::SimpleAtod("NaN", &d)); EXPECT_TRUE(absl::SimpleAtod("NaN", &d));
...@@ -412,12 +424,116 @@ TEST(NumbersTest, Atod) { ...@@ -412,12 +424,116 @@ TEST(NumbersTest, Atod) {
EXPECT_EQ(d, 1.7976931348623157e+308); EXPECT_EQ(d, 1.7976931348623157e+308);
EXPECT_TRUE(absl::SimpleAtod("5e308", &d)); EXPECT_TRUE(absl::SimpleAtod("5e308", &d));
EXPECT_TRUE(std::isinf(d) && (d > 0)); EXPECT_TRUE(std::isinf(d) && (d > 0));
// Ditto, but for FLT_MAX.
// Parse DBL_MIN (normal) and DBL_TRUE_MIN (subnormal). EXPECT_TRUE(absl::SimpleAtof("3.4028234663852886e+38", &f));
EXPECT_EQ(f, 3.4028234663852886e+38f);
EXPECT_TRUE(absl::SimpleAtof("7e38", &f));
EXPECT_TRUE(std::isinf(f) && (f > 0));
// Parse the largest N such that parsing 1eN produces a finite value and the
// smallest M = N + 1 such that parsing 1eM produces infinity.
//
// The 309 exponent (and 39) confirms the "definition of
// kEiselLemireMaxExclExp10" comment in charconv.cc.
EXPECT_TRUE(absl::SimpleAtod("1e308", &d));
EXPECT_EQ(d, 1e308);
EXPECT_FALSE(std::isinf(d));
EXPECT_TRUE(absl::SimpleAtod("1e309", &d));
EXPECT_TRUE(std::isinf(d));
// Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("1e38", &f));
EXPECT_EQ(f, 1e38f);
EXPECT_FALSE(std::isinf(f));
EXPECT_TRUE(absl::SimpleAtof("1e39", &f));
EXPECT_TRUE(std::isinf(f));
// Parse the largest N such that parsing 9.999999999999999999eN, with 19
// nines, produces a finite value.
//
// 9999999999999999999, with 19 nines but no decimal point, is the largest
// "repeated nines" integer that fits in a uint64_t.
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e307", &d));
EXPECT_EQ(d, 9.999999999999999999e307);
EXPECT_FALSE(std::isinf(d));
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e308", &d));
EXPECT_TRUE(std::isinf(d));
// Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e37", &f));
EXPECT_EQ(f, 9.999999999999999999e37f);
EXPECT_FALSE(std::isinf(f));
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e38", &f));
EXPECT_TRUE(std::isinf(f));
// Parse DBL_MIN (normal), DBL_TRUE_MIN (subnormal) and (DBL_TRUE_MIN / 10)
// (effectively zero).
EXPECT_TRUE(absl::SimpleAtod("2.2250738585072014e-308", &d)); EXPECT_TRUE(absl::SimpleAtod("2.2250738585072014e-308", &d));
EXPECT_EQ(d, 2.2250738585072014e-308); EXPECT_EQ(d, 2.2250738585072014e-308);
EXPECT_TRUE(absl::SimpleAtod("4.9406564584124654e-324", &d)); EXPECT_TRUE(absl::SimpleAtod("4.9406564584124654e-324", &d));
EXPECT_EQ(d, 4.9406564584124654e-324); EXPECT_EQ(d, 4.9406564584124654e-324);
EXPECT_TRUE(absl::SimpleAtod("4.9406564584124654e-325", &d));
EXPECT_EQ(d, 0);
// Ditto, but for FLT_MIN, FLT_TRUE_MIN and (FLT_TRUE_MIN / 10).
EXPECT_TRUE(absl::SimpleAtof("1.1754943508222875e-38", &f));
EXPECT_EQ(f, 1.1754943508222875e-38f);
EXPECT_TRUE(absl::SimpleAtof("1.4012984643248171e-45", &f));
EXPECT_EQ(f, 1.4012984643248171e-45f);
EXPECT_TRUE(absl::SimpleAtof("1.4012984643248171e-46", &f));
EXPECT_EQ(f, 0);
// Parse the largest N (the most negative -N) such that parsing 1e-N produces
// a normal or subnormal (but still positive) or zero value.
EXPECT_TRUE(absl::SimpleAtod("1e-307", &d));
EXPECT_EQ(d, 1e-307);
EXPECT_GE(d, DBL_MIN);
EXPECT_LT(d, DBL_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("1e-323", &d));
EXPECT_EQ(d, 1e-323);
EXPECT_GE(d, DBL_TRUE_MIN);
EXPECT_LT(d, DBL_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("1e-324", &d));
EXPECT_EQ(d, 0);
// Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("1e-37", &f));
EXPECT_EQ(f, 1e-37f);
EXPECT_GE(f, FLT_MIN);
EXPECT_LT(f, FLT_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("1e-45", &f));
EXPECT_EQ(f, 1e-45f);
EXPECT_GE(f, FLT_TRUE_MIN);
EXPECT_LT(f, FLT_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("1e-46", &f));
EXPECT_EQ(f, 0);
// Parse the largest N (the most negative -N) such that parsing
// 9.999999999999999999e-N, with 19 nines, produces a normal or subnormal
// (but still positive) or zero value.
//
// 9999999999999999999, with 19 nines but no decimal point, is the largest
// "repeated nines" integer that fits in a uint64_t.
//
// The -324/-325 exponents (and -46/-47) confirms the "definition of
// kEiselLemireMinInclExp10" comment in charconv.cc.
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e-308", &d));
EXPECT_EQ(d, 9.999999999999999999e-308);
EXPECT_GE(d, DBL_MIN);
EXPECT_LT(d, DBL_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e-324", &d));
EXPECT_EQ(d, 9.999999999999999999e-324);
EXPECT_GE(d, DBL_TRUE_MIN);
EXPECT_LT(d, DBL_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtod("9.999999999999999999e-325", &d));
EXPECT_EQ(d, 0);
// Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e-38", &f));
EXPECT_EQ(f, 9.999999999999999999e-38f);
EXPECT_GE(f, FLT_MIN);
EXPECT_LT(f, FLT_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e-46", &f));
EXPECT_EQ(f, 9.999999999999999999e-46f);
EXPECT_GE(f, FLT_TRUE_MIN);
EXPECT_LT(f, FLT_TRUE_MIN * 10);
EXPECT_TRUE(absl::SimpleAtof("9.999999999999999999e-47", &f));
EXPECT_EQ(f, 0);
// Leading and/or trailing whitespace is OK. // Leading and/or trailing whitespace is OK.
EXPECT_TRUE(absl::SimpleAtod(" \t\r\n 2.718", &d)); EXPECT_TRUE(absl::SimpleAtod(" \t\r\n 2.718", &d));
...@@ -459,6 +575,13 @@ TEST(NumbersTest, Atod) { ...@@ -459,6 +575,13 @@ TEST(NumbersTest, Atod) {
EXPECT_EQ(d, 1e+23); EXPECT_EQ(d, 1e+23);
EXPECT_TRUE(absl::SimpleAtod("9223372036854775807", &d)); EXPECT_TRUE(absl::SimpleAtod("9223372036854775807", &d));
EXPECT_EQ(d, 9223372036854775807); EXPECT_EQ(d, 9223372036854775807);
// Ditto, but for Atof instead of Atod.
EXPECT_TRUE(absl::SimpleAtof("0.0625", &f));
EXPECT_EQ(f, 0.0625f);
EXPECT_TRUE(absl::SimpleAtof("20040229.0", &f));
EXPECT_EQ(f, 20040229.0f);
EXPECT_TRUE(absl::SimpleAtof("2147483647.0", &f));
EXPECT_EQ(f, 2147483647.0f);
// Some parsing algorithms don't always round correctly (but absl::SimpleAtod // Some parsing algorithms don't always round correctly (but absl::SimpleAtod
// should). This test case comes from // should). This test case comes from
...@@ -467,6 +590,8 @@ TEST(NumbersTest, Atod) { ...@@ -467,6 +590,8 @@ TEST(NumbersTest, Atod) {
// See also atod_manual_test.cc for running many more test cases. // See also atod_manual_test.cc for running many more test cases.
EXPECT_TRUE(absl::SimpleAtod("122.416294033786585", &d)); EXPECT_TRUE(absl::SimpleAtod("122.416294033786585", &d));
EXPECT_EQ(d, 122.416294033786585); EXPECT_EQ(d, 122.416294033786585);
EXPECT_TRUE(absl::SimpleAtof("122.416294033786585", &f));
EXPECT_EQ(f, 122.416294033786585f);
} }
TEST(NumbersTest, Prefixes) { TEST(NumbersTest, Prefixes) {
......
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