mirror of
https://github.com/nlohmann/json.git
synced 2026-05-16 03:05:23 +00:00
Fix for printing long doubles bug in dump_float (#3929)
This commit is contained in:
@@ -836,7 +836,7 @@ class serializer
|
||||
static int snprintf_float(char* buf, std::size_t size, int d, long double x)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
return (std::snprintf)(buf, size, "%.*lg", d, x);
|
||||
return (std::snprintf)(buf, size, "%.*Lg", d, x);
|
||||
}
|
||||
|
||||
void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
|
||||
|
||||
@@ -19828,7 +19828,7 @@ class serializer
|
||||
static int snprintf_float(char* buf, std::size_t size, int d, long double x)
|
||||
{
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
||||
return (std::snprintf)(buf, size, "%.*lg", d, x);
|
||||
return (std::snprintf)(buf, size, "%.*Lg", d, x);
|
||||
}
|
||||
|
||||
void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using nlohmann::json;
|
||||
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
@@ -295,3 +296,73 @@ TEST_CASE("dump with binary values")
|
||||
"]");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("dump for basic_json with long double number_float_t")
|
||||
{
|
||||
// Custom basic_json instantiation with long double as NumberFloatType.
|
||||
// On platforms where long double is wider than double (e.g. GCC/Clang on
|
||||
// Linux/macOS x86_64), dump() goes through the snprintf path in
|
||||
// serializer::dump_float(x, std::false_type). That branch must use the
|
||||
// "%.*Lg" format specifier; using "%.*g" with a long double argument is
|
||||
// undefined behavior and corrupts the output.
|
||||
using long_double_json = nlohmann::basic_json<std::map, std::vector, std::string,
|
||||
bool, std::int64_t, std::uint64_t, long double>;
|
||||
|
||||
SECTION("round-trip dump/parse")
|
||||
{
|
||||
constexpr std::array<long double, 13> values =
|
||||
{{
|
||||
0.0L, -0.0L, 1.0L, -1.0L,
|
||||
0.5L, -0.5L, 1.5L, -2.25L,
|
||||
1.23e45L, 1.23e-45L,
|
||||
(std::numeric_limits<long double>::min)(),
|
||||
std::numeric_limits<long double>::lowest(),
|
||||
(std::numeric_limits<long double>::max)()
|
||||
}};
|
||||
|
||||
for (long double v : values)
|
||||
{
|
||||
const long_double_json j = v;
|
||||
const auto s = j.dump();
|
||||
const auto j2 = long_double_json::parse(s);
|
||||
CHECK(j2.template get<long double>() == v);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("exact dump string for simple values")
|
||||
{
|
||||
CHECK(long_double_json(0.5L).dump() == "0.5");
|
||||
CHECK(long_double_json(-0.5L).dump() == "-0.5");
|
||||
CHECK(long_double_json(1.5L).dump() == "1.5");
|
||||
CHECK(long_double_json(-2.25L).dump() == "-2.25");
|
||||
CHECK(long_double_json(0.0L).dump() == "0.0");
|
||||
CHECK(long_double_json(1.0L).dump() == "1.0");
|
||||
CHECK(long_double_json(-1.0L).dump() == "-1.0");
|
||||
CHECK(long_double_json(100.0L).dump() == "100.0");
|
||||
}
|
||||
|
||||
SECTION("NaN and infinity dump as null")
|
||||
{
|
||||
CHECK(long_double_json(std::numeric_limits<long double>::quiet_NaN()).dump() == "null");
|
||||
CHECK(long_double_json(std::numeric_limits<long double>::infinity()).dump() == "null");
|
||||
CHECK(long_double_json(-std::numeric_limits<long double>::infinity()).dump() == "null");
|
||||
}
|
||||
|
||||
SECTION("dump output matches double for exactly-representable values")
|
||||
{
|
||||
auto check_same = [](long double v_ld, double v_d)
|
||||
{
|
||||
const long_double_json j_ld = v_ld;
|
||||
const json j_d = v_d;
|
||||
CHECK(j_ld.dump() == j_d.dump());
|
||||
};
|
||||
|
||||
check_same(0.0L, 0.0);
|
||||
check_same(0.5L, 0.5);
|
||||
check_same(-0.5L, -0.5);
|
||||
check_same(1.5L, 1.5);
|
||||
check_same(-2.25L, -2.25);
|
||||
check_same(1.0L, 1.0);
|
||||
check_same(100.0L, 100.0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user