diff --git a/include/nlohmann/detail/input/number_parse.hpp b/include/nlohmann/detail/input/number_parse.hpp index 4da712f81..29be3a67e 100644 --- a/include/nlohmann/detail/input/number_parse.hpp +++ b/include/nlohmann/detail/input/number_parse.hpp @@ -9,6 +9,7 @@ #pragma once #include // array +#include // FLT_EVAL_METHOD #include // size_t #include // int64_t, uint64_t #include // numeric_limits @@ -120,6 +121,19 @@ below). template bool parse_float_fast(const char* first, const char* last, DecimalPointType decimal_point, double& out) noexcept { +#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0 + // Clinger's fast path is only exact when double operations are evaluated in + // true double precision. On platforms that keep intermediates in extended + // precision (e.g. the x87 FPU on 32-bit x86, where FLT_EVAL_METHOD == 2) the + // single significand * 10^scale step is double-rounded and can be 1 ULP off, + // so decline and let the caller fall back to the correctly-rounded + // std::from_chars / std::strtod path. + static_cast(first); + static_cast(last); + static_cast(decimal_point); + static_cast(out); + return false; +#else static const std::array powers_of_ten = { { @@ -236,6 +250,7 @@ bool parse_float_fast(const char* first, const char* last, DecimalPointType deci } out = negative ? -result : result; return true; +#endif } /// fast float path is only exact for `double`; decline for float/long double diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index f40630517..f7dd2b705 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -7791,6 +7791,7 @@ NLOHMANN_JSON_NAMESPACE_END #include // array +#include // FLT_EVAL_METHOD #include // size_t #include // int64_t, uint64_t #include // numeric_limits @@ -7903,6 +7904,19 @@ below). template bool parse_float_fast(const char* first, const char* last, DecimalPointType decimal_point, double& out) noexcept { +#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0 + // Clinger's fast path is only exact when double operations are evaluated in + // true double precision. On platforms that keep intermediates in extended + // precision (e.g. the x87 FPU on 32-bit x86, where FLT_EVAL_METHOD == 2) the + // single significand * 10^scale step is double-rounded and can be 1 ULP off, + // so decline and let the caller fall back to the correctly-rounded + // std::from_chars / std::strtod path. + static_cast(first); + static_cast(last); + static_cast(decimal_point); + static_cast(out); + return false; +#else static const std::array powers_of_ten = { { @@ -8019,6 +8033,7 @@ bool parse_float_fast(const char* first, const char* last, DecimalPointType deci } out = negative ? -result : result; return true; +#endif } /// fast float path is only exact for `double`; decline for float/long double