Files
json/docs/mkdocs/docs/api/basic_json/number_float_t.md
Niels Lohmann 6b4b825af2 Handle numbers that do not fit narrow number types in the binary readers (#5607)
* Handle numbers that do not fit narrow number types in the binary readers

With custom number types narrower than the values in a binary document,
for example basic_json<..., std::int32_t, std::uint32_t, float>, every
binary reader (CBOR, MessagePack, UBJSON, BJData, BSON, BON8) passed the
decoded number to the SAX interface with an implicit conversion: the
integer 5000000000 silently became 705032704, and a finite double such as
1e300 became infinity. The lexer handles the same values in JSON text: an
integer that fits neither integer type is stored as number_float_t, and a
finite number that overflows number_float_t is rejected with
out_of_range.406.

Pass every number read from binary input through three helpers that
apply the lexer's rules:
- emit_signed(): number_integer_t, else number_unsigned_t for a
  non-negative value, else number_float_t
- emit_unsigned(): number_unsigned_t, else number_float_t
- emit_float(): out_of_range.406 if a finite value overflows
  number_float_t; infinity and NaN are passed on

For consistency, a CBOR negative integer below the range of
number_integer_t is now stored as number_float_t, like a too small
integer in JSON text, instead of being rejected with parse_error.112.
With the default number types, this is the only change in behavior.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix MSVC and clang 3.5 in the narrow number type test

MSVC types 3000000000 and 5000000000 as unsigned long, so
json(-3000000000) triggered C4146 (unary minus on an unsigned type),
which /WX turns into an error. Use LL literals, as elsewhere in the
tests.

clang 3.5 cannot convert the lambdas in the braced initializer of the
format table to function pointers. Use named functions instead.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Check integer-to-float fallbacks for overflow in the binary readers

emit_signed, emit_unsigned, and the CBOR negative integer fallback now
pass their number_float_t fallback through emit_float, so a value that
overflows number_float_t is rejected with out_of_range.406 like a
floating-point value, instead of silently becoming infinity. This only
matters for a number_float_t that cannot represent 2^64, such as a
half-precision type. The CBOR value -1 - n is computed as long double so
that emit_float sees a finite value.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Use the input_format member instead of passing the format to binary_reader helpers

The helpers (get_number, get_to, get_string, get_binary, get_bytes,
emit_signed, emit_unsigned, emit_float, unexpect_eof, exception_message)
are members of binary_reader, which already stores the format it was
constructed with, so the parameter was redundant.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-10-04 17:53:29 +02:00

4.1 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. Parsing and serialization are implemented in terms of #!cpp std::strtof/#!cpp std::strtod/#!cpp std::strtold and #!cpp std::snprintf, so the type must be #!cpp float, #!cpp double, or #!cpp long double. The binary formats additionally require #!cpp float or #!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.2 will be serialized to 1.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

Version history

  • Added in version 1.0.0.