## Numbers

JSON numbers are represented using `std::int64_t`, `std::uint64_t`, and `double`. When a `value` is constructed from an unsigned integer, its `kind` will be `kind::uint64`. Likewise, a `value` constructed from a signed integer will have `kind::int64`, or `kind::double_` if constructed from a floating-point type:

    // construction from int
    value jv1 = 1;

    assert( jv1.is_int64() );

    // construction from unsigned int
    value jv2 = 2u;

    assert( jv2.is_uint64() );

    // construction from double
    value jv3 = 3.0;

    assert( jv3.is_double() );

When accessing a number contained within a `value`, the function used must match the value’s `kind` exactly; no conversions will be performed. For example if `as_double` is called on a `value` that contains a `std::uint64_t`, an exception is thrown. Similarly, the function `if_double` will return `nullptr` and calling `get_double` will result in undefined behavior:

    value jv = 1;

    assert( jv.is_int64() );

    // jv.kind() != kind::uint64; throws
    std::uint64_t r1 = jv.as_uint64();

    // jv.kind() != kind::uint64; the behavior is undefined
    std::uint64_t r2 = jv.get_uint64();

    // if_double will always return nullptr, branch is not taken
    if(double* d = jv.if_double())
        assert( false );

In cases where you know that a `value` contains a number but don’t know its `kind`, `value::to_number` can be used to convert the `value` to an arithmetic type:

    value jv = 1;
    assert( jv.to_number< int >() == 1 );

If the `value` does not contain a number, or if the conversion is to an integer type `T` and the number cannot be represented exactly by `T`, the conversion will fail. Otherwise, the result is the number converted to `T` as-if by `static_cast`:

    value jv1 = 404;

    assert( jv1.is_int64() );

    // ok, identity conversion
    std::int64_t r1 = jv1.to_number< std::int64_t >( );

    // loss of data, throws system_error
    char r2 = jv1.to_number< char >();

    // ok, no loss of data
    double r3 = jv1.to_number< double >();

    value jv2 = 1.23;

    assert( jv1.is_double() );

    // ok, same as static_cast<float>( jv2.get_double() )
    float r4 = jv2.to_number< float >();

    // not exact, throws system_error
    int r5 = jv2.to_number< int >();

    value jv3 = {1, 2, 3};

    assert( ! jv3.is_number() );

    // not a number, throws system_error
    int r6 = jv3.to_number< int >();

In settings where exceptions cannot be used, an overload of `value::to_number` accepting `error_code` can be used instead with identical semantics to its throwing counterpart:

    value jv = 10.5;

    boost::system::error_code ec;

    // ok, conversion is exact
    float r1 = jv.to_number< float >( ec );

    assert( ! ec );

    // error, conversion is non-exact
    int r2 = jv.to_number< int >( ec );

    assert( ec == error::not_exact );

When parsing a JSON document, the type used to represent a number is not explicitly specified and must be determined from its value. In general, the parser will choose the best type which can accurately store the number as it appears in the document. Integers (i.e. numbers without decimals or exponents) that cannot be represented by `std::uint64_t` and `std::int64_t` will be represented as `double` to preserve their magnitude:

    value jv = parse("[-42, 100, 10.25, -299999999999999999998, 2e32]");

    array ja = jv.as_array();

    // represented by std::int64_t
    assert( ja[0].is_int64() );

    // represented by std::int64_t
    assert( ja[1].is_int64() );

    // contains decimal point, represented as double
    assert( ja[2].is_double() );

    // less than INT64_MIN, represented as double
    assert( ja[3].is_double() );

    // contains exponent, represented as double
    assert( ja[4].is_double() );

More formally, if the number:

- contains a decimal point, or

- contains an exponent, or

- is negative and its value is less than `INT64_MIN`, or

- is positive and its value is greater than `UINT64_MAX`,

then its type is `double`. Otherwise, if the number is positive and its value is greater than `INT64_MAX`, then its type is `std::uint64_t`. All other numbers are parsed as `std::int64_t`.

Edit this page