diff options
author | Abseil Team <absl-team@google.com> | 2022-09-26 06:36:48 -0700 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2022-09-26 06:37:29 -0700 |
commit | 7f9c536c0a5e4719ea8978999de699a1faa35591 (patch) | |
tree | 75c40d8d4d8c7210a50e19797e09073f20998677 /absl/strings/numbers_test.cc | |
parent | 8b951b099955e0635409a7b4294b5e83e455af43 (diff) | |
download | abseil-7f9c536c0a5e4719ea8978999de699a1faa35591.tar.gz abseil-7f9c536c0a5e4719ea8978999de699a1faa35591.tar.bz2 abseil-7f9c536c0a5e4719ea8978999de699a1faa35591.zip |
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
Diffstat (limited to 'absl/strings/numbers_test.cc')
-rw-r--r-- | absl/strings/numbers_test.cc | 129 |
1 files changed, 127 insertions, 2 deletions
diff --git a/absl/strings/numbers_test.cc b/absl/strings/numbers_test.cc index 41e95b80..b3c098d1 100644 --- a/absl/strings/numbers_test.cc +++ b/absl/strings/numbers_test.cc @@ -19,6 +19,7 @@ #include <sys/types.h> #include <cfenv> // NOLINT(build/c++11) +#include <cfloat> #include <cinttypes> #include <climits> #include <cmath> @@ -388,7 +389,18 @@ TEST(NumbersTest, Atoi) { } 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; + float f; // NaN can be spelled in multiple ways. EXPECT_TRUE(absl::SimpleAtod("NaN", &d)); @@ -412,12 +424,116 @@ TEST(NumbersTest, Atod) { EXPECT_EQ(d, 1.7976931348623157e+308); EXPECT_TRUE(absl::SimpleAtod("5e308", &d)); EXPECT_TRUE(std::isinf(d) && (d > 0)); - - // Parse DBL_MIN (normal) and DBL_TRUE_MIN (subnormal). + // Ditto, but for FLT_MAX. + 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_EQ(d, 2.2250738585072014e-308); EXPECT_TRUE(absl::SimpleAtod("4.9406564584124654e-324", &d)); 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. EXPECT_TRUE(absl::SimpleAtod(" \t\r\n 2.718", &d)); @@ -459,6 +575,13 @@ TEST(NumbersTest, Atod) { EXPECT_EQ(d, 1e+23); EXPECT_TRUE(absl::SimpleAtod("9223372036854775807", &d)); 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 // should). This test case comes from @@ -467,6 +590,8 @@ TEST(NumbersTest, Atod) { // See also atod_manual_test.cc for running many more test cases. EXPECT_TRUE(absl::SimpleAtod("122.416294033786585", &d)); EXPECT_EQ(d, 122.416294033786585); + EXPECT_TRUE(absl::SimpleAtof("122.416294033786585", &f)); + EXPECT_EQ(f, 122.416294033786585f); } TEST(NumbersTest, Prefixes) { |