Convert the floats of json_view from the digit layout

The parser records where the integer digits, the fraction digits, and the
exponent of a float token are. For doubles with at most 19 digits, the
value is now read from that layout: the digits eight at a time, without
scanning the token, and rounded with Clinger's fast path where both
operands are exact, else with the Eisel-Lemire algorithm (which needs no
fallback for up to 19 digits). Both round correctly, so the values are
those of parse(); other tokens and types keep the library's conversion.

get<double>(), materialize(), dump(), and comparisons use it. Traversing
canada.json (111,000 floats, every number converted): 0.53 -> 0.86 GB/s.

Tests add tokens around the limits (19 and 20 digits, 2^53, 10^22) to the
bit-for-bit comparison with parse().

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-09-28 23:24:57 +02:00
parent 1c7041908d
commit bf6b5b4719
6 changed files with 208 additions and 7 deletions

View File

@@ -826,7 +826,12 @@ TEST_CASE("json_view values")
std::mt19937_64 rng(5295); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed)
std::vector<std::string> tokens = {"0.1", "-0.0", "1e308", "1.7976931348623157e308", "2.2250738585072011e-308", "4.9e-324", "5e-324",
"0.1000000000000000055511151231257827021181583404541015625", "123456789012345678901234567890",
"9007199254740993", "1.00000000000000011102230246251565404236316680908203125", "7.2057594037927933e16"
"9007199254740993", "1.00000000000000011102230246251565404236316680908203125", "7.2057594037927933e16",
// around the limits of the conversion from the digit layout: 19 and 20
// digits, and those of Clinger's fast path (2^53, 10^22)
"1234567890.123456789", "1234567890.1234567891", "0.0000000000000000001", "123456789012345678.9",
"9007199254740992.0", "9007199254740993.0", "9007199254740994.0", "1.5e22", "1.5e23", "15e-22", "15e-23",
"1e-400", "0.0e0", "-0.0e-5", "12E+3", "12e-0"
};
for (int i = 0; i < 20000; ++i)
{