* Speed up the lexer: own float parser, string scan, and \u table Give the library its own correctly rounded float converter for binary32 and binary64 (IEEE 754), and speed up the lexer's string and escape scanning. The converter splits a number token into sign, significand, and decimal exponent, then tries Clinger's fast path, then a templated Eisel-Lemire step, and falls back to an exact big-integer digit comparison for tokens with more than 19 significant digits whose two candidate values round differently. This replaces std::from_chars and strtod/strtof for both formats, so parsed values no longer depend on the C/C++ library or the current locale. The strtold fallback kept for other long double formats (x87, binary128) now also copies a multi-byte decimal point correctly, fixing #5660. eisel_lemire() and decimal_to_float() are always inlined so callers keep the whole conversion in their hot loop. The string-scanning kernels in string_scan.hpp find a stop byte with the trailing-zero count of the SWAR mask instead of a byte loop, and scalar_string_bulk_run() validates a run of multi-byte UTF-8 sequences one after another instead of re-searching after each one. get_codepoint() decodes a contiguous \uXXXX escape with one table lookup per byte instead of four range-checked get() calls; the streaming path and all error positions are unchanged. Adds 508 generated hard float-parsing cases with expected binary32 and binary64 bits, and kernel-comparison tests for the string scans and the escape table against byte-by-byte references. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI warnings in own float parser - pow5_table.hpp: pow5_128_largest_power was unused in this branch's own code (GCC -Werror=unused-const-variable); tie it to the table size with a static_assert instead of removing it, since a later branch in the stack (json-view/23-zmij) uses it. - number_parse.hpp: rename the local variable `copy` to `buffer` to satisfy cpplint's build/include_what_you_use check. - unit-class_lexer.cpp: extend the NOLINT list on the seeded mt19937 with bugprone-random-generator-seed, and parenthesize `8 * sizeof(Bits) - 1` for clang-tidy. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Load string scan words with memcpy and use MSVC bit-scan intrinsics Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Reword float conversion notes for the string type and number_float_t docs Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix clang-tidy 22 findings in unit-class_lexer.cpp Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Mark the unsigned long of the MSVC bit scans for cpplint Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate the amalgamated headers Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Drop the position updates in the bulk \u path, which #5809 made unused Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: std::any_of in has_nonzero_digit, comment punctuation Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use data() instead of c_str() in the multi-byte decimal point path Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
4.3 KiB
nlohmann::basic_json::number_float_t
using number_float_t = NumberFloatType;
The type used to store JSON numbers (floating-point).
RFC 8259 describes numbers as follows:
The representation of numbers is similar to that used in most programming languages. A number is represented in base 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted.
This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is
known whether the number is a signed integer, an unsigned integer, or a floating-point number. Therefore, three different
types, number_integer_t, number_unsigned_t and number_float_t are
used.
To store floating-point numbers in C++, a type is defined by the template parameter NumberFloatType which chooses the
type to use.
Template parameters
NumberFloatType- the type to store floating-point numbers. The type must be
#!cpp float,#!cpp double, or#!cpp long double. The parser converts#!cpp float,#!cpp double, and a#!cpp long doublethat is IEEE 754 binary64 itself. It converts other#!cpp long doubleformats with#!cpp std::from_charswhere available, or with#!cpp std::strtoldotherwise. Serialization falls back to#!cpp std::snprintf. The binary formats additionally require#!cpp floator#!cpp double, because they have no encoding for#!cpp long double. See Template Parameter Requirements.
Notes
Default type
With the default values for NumberFloatType (double), the default value for number_float_t is #!cpp double.
Default behavior
- The restrictions about leading zeros are not enforced in C++. Instead, leading zeros in floating-point literals will
be ignored. Internally, the value will be stored as a decimal number. For instance, the C++ floating-point literal
01.2will be serialized to1.2. During deserialization, leading zeros yield an error. - Not-a-number (NaN) values will be serialized to
null.
Limits
RFC 8259 states:
This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754-2008 binary64 (double precision) numbers is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision.
This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note values
smaller than -1.79769313486232e+308 and values greater than 1.79769313486232e+308 will be stored as NaN internally
and be serialized to null.
During deserialization (from JSON text or any of the binary formats), a finite number that does not fit into
number_float_t is rejected with out_of_range.406, for
example a double-precision number in a binary format when number_float_t is #!cpp float.
Storage
Floating-point number values are stored directly inside a basic_json type.
Examples
??? example
The following code shows that `number_float_t` is by default, a typedef to `#!cpp double`.
```cpp
--8<-- "examples/number_float_t.cpp"
```
Output:
```json
--8<-- "examples/number_float_t.output"
```
See also
- number_integer_t the type used to store JSON integer numbers
- number_unsigned_t the type used to store JSON unsigned integer numbers
- is_number_float checks whether the JSON value is a floating-point number
- Number Handling - the article on number handling
Version history
- Added in version 1.0.0.