mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 06:25:17 +00:00
* Convert floats with Eisel-Lemire when std::from_chars is unavailable Float tokens that Clinger's fast path cannot convert (e.g. the 17-digit coordinates of canada.json) went to strtod unless std::from_chars was available. It is not used in C++11/14, and not with libc++, which does not define __cpp_lib_to_chars. The Eisel-Lemire algorithm (after fast_float's compute_float) now converts them with integer arithmetic, correctly rounded for any token with at most 19 significant digits. Longer tokens are truncated; the result is used if w and w + 1 round alike, else strtod decides as before. Overflow still yields infinity (out_of_range.406). The table of powers of five (fast_float's) lives in pow5_table.hpp; a unit test recomputes every entry with big-integer arithmetic. Further tests: known values generated with Python (whose float() is correctly rounded), 200,000 round trips through to_chars, and the 128-bit multiplication and leading-zero count against big-integer references (both with and without a 128-bit type). Checked against strtod on 6.5 million tokens, among them 60,000 exact halfway cases: no difference. json::parse on canada.json: -8.6% (C++11), -7.6% (C++17, Apple clang); other files unchanged. Compile time of a TU including json.hpp: +0.7%. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI: unused parse result and find() == npos in the Eisel-Lemire tests GCC (-Werror=unused-result) rejected CHECK_THROWS_WITH_AS(json::parse(...)) because parse() is [[nodiscard]]; assign the result to a dummy json as the other tests do. clang-tidy flagged longer.find('.') == npos with abseil-string-find-str-contains; store the position in a variable first. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
82 lines
2.7 KiB
C++
82 lines
2.7 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <cstdint> // uint64_t
|
|
|
|
#include <nlohmann/detail/abi_macros.hpp>
|
|
|
|
// Portable bit-level helpers for the number and string scanners. They use
|
|
// compiler builtins where available and plain C++ otherwise, so they need no
|
|
// platform headers and work regardless of byte order.
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
|
|
/// number of leading zero bits of x (x != 0)
|
|
inline int count_leading_zeros(std::uint64_t x) noexcept
|
|
{
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
return __builtin_clzll(x);
|
|
#else
|
|
int n = 0;
|
|
for (int shift = 32; shift != 0; shift >>= 1)
|
|
{
|
|
if ((x >> (64 - shift)) == 0)
|
|
{
|
|
n += shift;
|
|
x <<= shift;
|
|
}
|
|
}
|
|
return n;
|
|
#endif
|
|
}
|
|
|
|
/// the 128-bit product of two 64-bit numbers
|
|
struct uint128_parts
|
|
{
|
|
std::uint64_t low;
|
|
std::uint64_t high;
|
|
};
|
|
|
|
inline uint128_parts full_multiplication(std::uint64_t a, std::uint64_t b) noexcept
|
|
{
|
|
#if defined(__SIZEOF_INT128__)
|
|
__extension__ using uint128 = unsigned __int128;
|
|
const uint128 r = static_cast<uint128>(a) * b;
|
|
return {static_cast<std::uint64_t>(r), static_cast<std::uint64_t>(r >> 64u)};
|
|
#else
|
|
const std::uint64_t a_lo = a & 0xFFFFFFFFu;
|
|
const std::uint64_t a_hi = a >> 32u;
|
|
const std::uint64_t b_lo = b & 0xFFFFFFFFu;
|
|
const std::uint64_t b_hi = b >> 32u;
|
|
const std::uint64_t lo_lo = a_lo * b_lo;
|
|
const std::uint64_t hi_lo = a_hi * b_lo;
|
|
const std::uint64_t lo_hi = a_lo * b_hi;
|
|
const std::uint64_t hi_hi = a_hi * b_hi;
|
|
const std::uint64_t cross = (lo_lo >> 32u) + (hi_lo & 0xFFFFFFFFu) + lo_hi;
|
|
return {(cross << 32u) | (lo_lo & 0xFFFFFFFFu), (hi_lo >> 32u) + (cross >> 32u) + hi_hi};
|
|
#endif
|
|
}
|
|
|
|
/// eight bytes as a little-endian word (compilers fold this into one load on
|
|
/// little-endian targets)
|
|
inline std::uint64_t read_eight_bytes(const char* p) noexcept
|
|
{
|
|
const auto* b = reinterpret_cast<const unsigned char*>(p); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
|
|
return static_cast<std::uint64_t>(b[0]) | (static_cast<std::uint64_t>(b[1]) << 8u)
|
|
| (static_cast<std::uint64_t>(b[2]) << 16u) | (static_cast<std::uint64_t>(b[3]) << 24u)
|
|
| (static_cast<std::uint64_t>(b[4]) << 32u) | (static_cast<std::uint64_t>(b[5]) << 40u)
|
|
| (static_cast<std::uint64_t>(b[6]) << 48u) | (static_cast<std::uint64_t>(b[7]) << 56u);
|
|
}
|
|
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|