| // __ _____ _____ _____ | |
| // __| | __| | | | JSON for Modern C++ (supporting code) | |
| // | | |__ | | | | | | version 3.12.0 | |
| // |_____|_____|_____|_|___| https://github.com/nlohmann/json | |
| // | |
| // SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me> | |
| // SPDX-License-Identifier: MIT | |
| // XXX: | |
| // Only compile these tests if 'float' and 'double' are IEEE-754 single- and | |
| // double-precision numbers, resp. | |
| #include "doctest_compatibility.h" | |
| #include <nlohmann/json.hpp> | |
| using nlohmann::detail::bit_cast; | |
| #include <algorithm> | |
| #include <array> | |
| #include <cmath> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <limits> | |
| #include <random> | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #if defined(JSON_HAS_CPP_17) | |
| #include <charconv> | |
| #endif | |
| namespace | |
| { | |
| float make_float(uint32_t sign_bit, uint32_t biased_exponent, uint32_t significand) | |
| { | |
| assert(sign_bit == 0 || sign_bit == 1); | |
| assert(biased_exponent <= 0xFF); | |
| assert(significand <= 0x007FFFFF); | |
| uint32_t bits = 0; | |
| bits |= sign_bit << 31; | |
| bits |= biased_exponent << 23; | |
| bits |= significand; | |
| return bit_cast<float>(bits); | |
| } | |
| // ldexp -- convert f * 2^e to IEEE single precision | |
| float make_float(uint64_t f, int e) | |
| { | |
| constexpr uint64_t kHiddenBit = 0x00800000; | |
| constexpr uint64_t kSignificandMask = 0x007FFFFF; | |
| constexpr int kPhysicalSignificandSize = 23; // Excludes the hidden bit. | |
| constexpr int kExponentBias = 0x7F + kPhysicalSignificandSize; | |
| constexpr int kDenormalExponent = 1 - kExponentBias; | |
| constexpr int kMaxExponent = 0xFF - kExponentBias; | |
| while (f > kHiddenBit + kSignificandMask) | |
| { | |
| f >>= 1; | |
| e++; | |
| } | |
| if (e >= kMaxExponent) | |
| { | |
| return std::numeric_limits<float>::infinity(); | |
| } | |
| if (e < kDenormalExponent) | |
| { | |
| return 0.0; | |
| } | |
| while (e > kDenormalExponent && (f & kHiddenBit) == 0) | |
| { | |
| f <<= 1; | |
| e--; | |
| } | |
| const uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0) | |
| ? 0 | |
| : static_cast<uint64_t>(e + kExponentBias); | |
| const uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize); | |
| return bit_cast<float>(static_cast<uint32_t>(bits)); | |
| } | |
| double make_double(uint64_t sign_bit, uint64_t biased_exponent, uint64_t significand) | |
| { | |
| assert(sign_bit == 0 || sign_bit == 1); | |
| assert(biased_exponent <= 0x7FF); | |
| assert(significand <= 0x000FFFFFFFFFFFFF); | |
| uint64_t bits = 0; | |
| bits |= sign_bit << 63; | |
| bits |= biased_exponent << 52; | |
| bits |= significand; | |
| return bit_cast<double>(bits); | |
| } | |
| // ldexp -- convert f * 2^e to IEEE double precision | |
| double make_double(uint64_t f, int e) | |
| { | |
| constexpr uint64_t kHiddenBit = 0x0010000000000000; | |
| constexpr uint64_t kSignificandMask = 0x000FFFFFFFFFFFFF; | |
| constexpr int kPhysicalSignificandSize = 52; // Excludes the hidden bit. | |
| constexpr int kExponentBias = 0x3FF + kPhysicalSignificandSize; | |
| constexpr int kDenormalExponent = 1 - kExponentBias; | |
| constexpr int kMaxExponent = 0x7FF - kExponentBias; | |
| while (f > kHiddenBit + kSignificandMask) | |
| { | |
| f >>= 1; | |
| e++; | |
| } | |
| if (e >= kMaxExponent) | |
| { | |
| return std::numeric_limits<double>::infinity(); | |
| } | |
| if (e < kDenormalExponent) | |
| { | |
| return 0.0; | |
| } | |
| while (e > kDenormalExponent && (f & kHiddenBit) == 0) | |
| { | |
| f <<= 1; | |
| e--; | |
| } | |
| const uint64_t biased_exponent = (e == kDenormalExponent && (f & kHiddenBit) == 0) | |
| ? 0 | |
| : static_cast<uint64_t>(e + kExponentBias); | |
| const uint64_t bits = (f & kSignificandMask) | (biased_exponent << kPhysicalSignificandSize); | |
| return bit_cast<double>(bits); | |
| } | |
| } // namespace | |
| TEST_CASE("digit gen") | |
| { | |
| SECTION("single precision") | |
| { | |
| auto check_float = [](float number, const std::string & digits, int expected_exponent) | |
| { | |
| CAPTURE(number) | |
| CAPTURE(digits) | |
| CAPTURE(expected_exponent) | |
| std::array<char, 32> buf{}; | |
| int len = 0; | |
| int exponent = 0; | |
| nlohmann::detail::dtoa_impl::grisu2(buf.data(), len, exponent, number); | |
| CHECK(digits == std::string(buf.data(), buf.data() + len)); | |
| CHECK(expected_exponent == exponent); | |
| }; | |
| check_float(make_float(0, 0, 0x00000001), "1", -45); // min denormal | |
| check_float(make_float(0, 0, 0x007FFFFF), "11754942", -45); // max denormal | |
| check_float(make_float(0, 1, 0x00000000), "11754944", -45); // min normal | |
| check_float(make_float(0, 1, 0x00000001), "11754945", -45); | |
| check_float(make_float(0, 1, 0x007FFFFF), "23509886", -45); | |
| check_float(make_float(0, 2, 0x00000000), "23509887", -45); | |
| check_float(make_float(0, 2, 0x00000001), "2350989", -44); | |
| check_float(make_float(0, 24, 0x00000000), "98607613", -39); // fail if no special case in normalized boundaries | |
| check_float(make_float(0, 30, 0x00000000), "63108872", -37); // fail if no special case in normalized boundaries | |
| check_float(make_float(0, 31, 0x00000000), "12621775", -36); // fail if no special case in normalized boundaries | |
| check_float(make_float(0, 57, 0x00000000), "84703295", -29); // fail if no special case in normalized boundaries | |
| check_float(make_float(0, 254, 0x007FFFFE), "34028233", 31); | |
| check_float(make_float(0, 254, 0x007FFFFF), "34028235", 31); // max normal | |
| // V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal Conversion", manuscript, May 1991, | |
| // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z (report) | |
| // ftp://ftp.ee.lbl.gov/testbase.tar.Z (program) | |
| // Table 16: Stress Inputs for Converting 24-bit Binary to Decimal, < 1/2 ULP | |
| check_float(make_float(12676506, -102), "25", -25); | |
| check_float(make_float(12676506, -103), "125", -26); | |
| check_float(make_float(15445013, 86), "1195", 30); | |
| check_float(make_float(13734123, -138), "39415", -39); | |
| check_float(make_float(12428269, -130), "913085", -38); | |
| check_float(make_float(15334037, -146), "1719005", -43); | |
| check_float(make_float(11518287, -41), "52379105", -13); | |
| check_float(make_float(12584953, -145), "2821644", -43); | |
| check_float(make_float(15961084, -125), "37524328", -38); | |
| check_float(make_float(14915817, -146), "16721209", -44); | |
| check_float(make_float(10845484, -102), "21388946", -31); | |
| check_float(make_float(16431059, -61), "7125836", -18); | |
| // Table 17: Stress Inputs for Converting 24-bit Binary to Decimal, > 1/2 ULP | |
| check_float(make_float(16093626, 69), "95", 26); | |
| check_float(make_float( 9983778, 25), "335", 12); | |
| check_float(make_float(12745034, 104), "2585", 35); | |
| check_float(make_float(12706553, 72), "60005", 24); | |
| check_float(make_float(11005028, 45), "387205", 15); | |
| check_float(make_float(15059547, 71), "3555835", 22); | |
| check_float(make_float(16015691, -99), "25268305", -30); | |
| check_float(make_float( 8667859, 56), "6245851", 17); | |
| check_float(make_float(14855922, -82), "30721327", -25); | |
| check_float(make_float(14855922, -83), "15360663", -25); | |
| check_float(make_float(10144164, -110), "781478", -32); | |
| check_float(make_float(13248074, 95), "52481028", 28); | |
| } | |
| SECTION("double precision") | |
| { | |
| auto check_double = [](double number, const std::string & digits, int expected_exponent) | |
| { | |
| CAPTURE(number) | |
| CAPTURE(digits) | |
| CAPTURE(expected_exponent) | |
| std::array<char, 32> buf{}; | |
| int len = 0; | |
| int exponent = 0; | |
| nlohmann::detail::dtoa_impl::grisu2(buf.data(), len, exponent, number); | |
| CHECK(digits == std::string(buf.data(), buf.data() + len)); | |
| CHECK(expected_exponent == exponent); | |
| }; | |
| check_double(make_double(0, 0, 0x0000000000000001), "5", -324); // min denormal | |
| check_double(make_double(0, 0, 0x000FFFFFFFFFFFFF), "2225073858507201", -323); // max denormal | |
| check_double(make_double(0, 1, 0x0000000000000000), "22250738585072014", -324); // min normal | |
| check_double(make_double(0, 1, 0x0000000000000001), "2225073858507202", -323); | |
| check_double(make_double(0, 1, 0x000FFFFFFFFFFFFF), "44501477170144023", -324); | |
| check_double(make_double(0, 2, 0x0000000000000000), "4450147717014403", -323); | |
| check_double(make_double(0, 2, 0x0000000000000001), "4450147717014404", -323); | |
| check_double(make_double(0, 4, 0x0000000000000000), "17800590868057611", -323); // fail if no special case in normalized boundaries | |
| check_double(make_double(0, 5, 0x0000000000000000), "35601181736115222", -323); // fail if no special case in normalized boundaries | |
| check_double(make_double(0, 6, 0x0000000000000000), "7120236347223045", -322); // fail if no special case in normalized boundaries | |
| check_double(make_double(0, 10, 0x0000000000000000), "11392378155556871", -321); // fail if no special case in normalized boundaries | |
| check_double(make_double(0, 2046, 0x000FFFFFFFFFFFFE), "17976931348623155", 292); | |
| check_double(make_double(0, 2046, 0x000FFFFFFFFFFFFF), "17976931348623157", 292); // max normal | |
| // Test different paths in DigitGen | |
| check_double( 10000, "1", 4); | |
| check_double( 1200000, "12", 5); | |
| check_double(4.9406564584124654e-324, "5", -324); // exit integral loop | |
| check_double(2.2250738585072009e-308, "2225073858507201", -323); // exit fractional loop | |
| check_double( 1.82877982605164e-99, "182877982605164", -113); | |
| check_double( 1.1505466208671903e-09, "11505466208671903", -25); | |
| check_double( 5.5645893133766722e+20, "5564589313376672", 5); | |
| check_double( 53.034830388866226, "53034830388866226", -15); | |
| check_double( 0.0021066531670178605, "21066531670178605", -19); | |
| // V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal Conversion", manuscript, May 1991, | |
| // ftp://ftp.ee.lbl.gov/testbase-report.ps.Z (report) | |
| // ftp://ftp.ee.lbl.gov/testbase.tar.Z (program) | |
| // Table 3: Stress Inputs for Converting 53-bit Binary to Decimal, < 1/2 ULP | |
| check_double(make_double(8511030020275656, -342) /* 9.5e-088 */, "95", -89); | |
| check_double(make_double(5201988407066741, -824) /* 4.65e-233 */, "465", -235); | |
| check_double(make_double(6406892948269899, +237) /* 1.415e+087 */, "1415", 84); | |
| check_double(make_double(8431154198732492, +72) /* 3.9815e+037 */, "39815", 33); | |
| check_double(make_double(6475049196144587, +99) /* 4.10405e+045 */, "410405", 40); | |
| check_double(make_double(8274307542972842, +726) /* 2.920845e+234 */, "2920845", 228); | |
| check_double(make_double(5381065484265332, -456) /* 2.8919465e-122 */, "28919465", -129); | |
| check_double(make_double(6761728585499734, -1057) /* 4.37877185e-303 */, "437877185", -311); | |
| check_double(make_double(7976538478610756, +376) /* 1.227701635e+129 */, "1227701635", 120); | |
| check_double(make_double(5982403858958067, +377) /* 1.8415524525e+129 */, "18415524525", 119); | |
| check_double(make_double(5536995190630837, +93) /* 5.48357443505e+043 */, "548357443505", 32); | |
| check_double(make_double(7225450889282194, +710) /* 3.891901811465e+229 */, "3891901811465", 217); | |
| check_double(make_double(7225450889282194, +709) /* 1.9459509057325e+229 */, "19459509057325", 216); | |
| check_double(make_double(8703372741147379, +117) /* 1.44609583816055e+051 */, "144609583816055", 37); | |
| check_double(make_double(8944262675275217, -1001) /* 4.173677474585315e-286 */, "4173677474585315", -301); | |
| check_double(make_double(7459803696087692, -707) /* 1.1079507728788885e-197 */, "11079507728788885", -213); | |
| check_double(make_double(6080469016670379, -381) /* 1.234550136632744e-099 */, "1234550136632744", -114); | |
| check_double(make_double(8385515147034757, +721) /* 9.2503171196036502e+232 */, "925031711960365", 218); | |
| check_double(make_double(7514216811389786, -828) /* 4.1980471502848898e-234 */, "419804715028489", -248); | |
| check_double(make_double(8397297803260511, -345) /* 1.1716315319786511e-088 */, "11716315319786511", -104); | |
| check_double(make_double(6733459239310543, +202) /* 4.3281007284461249e+076 */, "4328100728446125", 61); | |
| check_double(make_double(8091450587292794, -473) /* 3.3177101181600311e-127 */, "3317710118160031", -142); | |
| // Table 4: Stress Inputs for Converting 53-bit Binary to Decimal, > 1/2 ULP | |
| check_double(make_double(6567258882077402, +952) /* 2.5e+302 */, "25", 301); | |
| check_double(make_double(6712731423444934, +535) /* 7.55e+176 */, "755", 174); | |
| check_double(make_double(6712731423444934, +534) /* 3.775e+176 */, "3775", 173); | |
| check_double(make_double(5298405411573037, -957) /* 4.3495e-273 */, "43495", -277); | |
| check_double(make_double(5137311167659507, -144) /* 2.30365e-028 */, "230365", -33); | |
| check_double(make_double(6722280709661868, +363) /* 1.263005e+125 */, "1263005", 119); | |
| check_double(make_double(5344436398034927, -169) /* 7.1422105e-036 */, "71422105", -43); | |
| check_double(make_double(8369123604277281, -853) /* 1.39345735e-241 */, "139345735", -249); | |
| check_double(make_double(8995822108487663, -780) /* 1.414634485e-219 */, "1414634485", -228); | |
| check_double(make_double(8942832835564782, -383) /* 4.5392779195e-100 */, "45392779195", -110); | |
| check_double(make_double(8942832835564782, -384) /* 2.26963895975e-100 */, "226963895975", -111); | |
| check_double(make_double(8942832835564782, -385) /* 1.134819479875e-100 */, "1134819479875", -112); | |
| check_double(make_double(6965949469487146, -249) /* 7.7003665618895e-060 */, "77003665618895", -73); | |
| check_double(make_double(6965949469487146, -250) /* 3.85018328094475e-060 */, "385018328094475", -74); | |
| check_double(make_double(6965949469487146, -251) /* 1.925091640472375e-060 */, "1925091640472375", -75); | |
| check_double(make_double(7487252720986826, +548) /* 6.8985865317742005e+180 */, "68985865317742005", 164); | |
| check_double(make_double(5592117679628511, +164) /* 1.3076622631878654e+065 */, "13076622631878654", 49); | |
| check_double(make_double(8887055249355788, +665) /* 1.3605202075612124e+216 */, "13605202075612124", 200); | |
| check_double(make_double(6994187472632449, +690) /* 3.5928102174759597e+223 */, "35928102174759597", 207); | |
| check_double(make_double(8797576579012143, +588) /* 8.9125197712484552e+192 */, "8912519771248455", 177); | |
| check_double(make_double(7363326733505337, +272) /* 5.5876975736230114e+097 */, "55876975736230114", 81); | |
| check_double(make_double(8549497411294502, -448) /* 1.1762578307285404e-119 */, "11762578307285404", -135); | |
| // Table 20: Stress Inputs for Converting 56-bit Binary to Decimal, < 1/2 ULP | |
| check_double(make_double(50883641005312716, -172) /* 8.4999999999999993e-036 */, "8499999999999999", -51); | |
| check_double(make_double(38162730753984537, -170) /* 2.5499999999999999e-035 */, "255", -37); | |
| check_double(make_double(50832789069151999, -101) /* 2.0049999999999997e-014 */, "20049999999999997", -30); | |
| check_double(make_double(51822367833714164, -109) /* 7.9844999999999994e-017 */, "7984499999999999", -32); | |
| check_double(make_double(66840152193508133, -172) /* 1.1165499999999999e-035 */, "11165499999999999", -51); | |
| check_double(make_double(55111239245584393, -138) /* 1.581615e-025 */, "1581615", -31); | |
| check_double(make_double(71704866733321482, -112) /* 1.3809855e-017 */, "13809855", -24); | |
| check_double(make_double(67160949328233173, -142) /* 1.2046404499999999e-026 */, "12046404499999999", -42); | |
| check_double(make_double(53237141308040189, -152) /* 9.3251405449999991e-030 */, "9325140544999999", -45); | |
| check_double(make_double(62785329394975786, -112) /* 1.2092014595e-017 */, "12092014595", -27); | |
| check_double(make_double(48367680154689523, -77) /* 3.2007045838499998e-007 */, "320070458385", -18); | |
| check_double(make_double(42552223180606797, -102) /* 8.391946324354999e-015 */, "8391946324354999", -30); | |
| check_double(make_double(63626356173011241, -112) /* 1.2253990460585e-017 */, "12253990460585", -30); | |
| check_double(make_double(43566388595783643, -99) /* 6.8735641489760495e-014 */, "687356414897605", -28); | |
| check_double(make_double(54512669636675272, -159) /* 7.459816430480385e-032 */, "7459816430480385", -47); | |
| check_double(make_double(52306490527514614, -167) /* 2.7960588398142552e-034 */, "2796058839814255", -49); | |
| check_double(make_double(52306490527514614, -168) /* 1.3980294199071276e-034 */, "13980294199071276", -50); | |
| check_double(make_double(41024721590449423, -89) /* 6.6279012373057359e-011 */, "6627901237305736", -26); | |
| check_double(make_double(37664020415894738, -132) /* 6.9177880043968072e-024 */, "6917788004396807", -39); | |
| check_double(make_double(37549883692866294, -93) /* 3.7915693108349708e-012 */, "3791569310834971", -27); | |
| check_double(make_double(69124110374399839, -104) /* 3.4080817676591365e-015 */, "34080817676591365", -31); | |
| check_double(make_double(69124110374399839, -105) /* 1.7040408838295683e-015 */, "17040408838295683", -31); | |
| // Table 21: Stress Inputs for Converting 56-bit Binary to Decimal, > 1/2 ULP | |
| check_double(make_double(49517601571415211, -94) /* 2.4999999999999998e-012 */, "25", -13); | |
| check_double(make_double(49517601571415211, -95) /* 1.2499999999999999e-012 */, "125", -14); | |
| check_double(make_double(54390733528642804, -133) /* 4.9949999999999996e-024 */, "49949999999999996", -40); // shortest: 4995e-27 | |
| check_double(make_double(71805402319113924, -157) /* 3.9304999999999998e-031 */, "39304999999999998", -47); // shortest: 39305e-35 | |
| check_double(make_double(40435277969631694, -179) /* 5.2770499999999992e-038 */, "5277049999999999", -53); | |
| check_double(make_double(57241991568619049, -165) /* 1.223955e-033 */, "1223955", -39); | |
| check_double(make_double(65224162876242886, +58) /* 1.8799584999999998e+034 */, "18799584999999998", 18); | |
| check_double(make_double(70173376848895368, -138) /* 2.01387715e-025 */, "201387715", -33); | |
| check_double(make_double(37072848117383207, -99) /* 5.8490641049999989e-014 */, "5849064104999999", -29); | |
| check_double(make_double(56845051585389697, -176) /* 5.9349003054999999e-037 */, "59349003055", -47); | |
| check_double(make_double(54791673366936431, -145) /* 1.2284718039499998e-027 */, "12284718039499998", -43); | |
| check_double(make_double(66800318669106231, -169) /* 8.9270767180849991e-035 */, "8927076718084999", -50); | |
| check_double(make_double(66800318669106231, -170) /* 4.4635383590424995e-035 */, "44635383590424995", -51); | |
| check_double(make_double(66574323440112438, -119) /* 1.0016990862549499e-019 */, "10016990862549499", -35); | |
| check_double(make_double(65645179969330963, -173) /* 5.4829412628024647e-036 */, "5482941262802465", -51); | |
| check_double(make_double(61847254334681076, -109) /* 9.5290783281036439e-017 */, "9529078328103644", -32); | |
| check_double(make_double(39990712921393606, -145) /* 8.9662279366405553e-028 */, "8966227936640555", -43); | |
| check_double(make_double(59292318184400283, -149) /* 8.3086234418058538e-029 */, "8308623441805854", -44); | |
| check_double(make_double(69116558615326153, -143) /* 6.1985873566126555e-027 */, "61985873566126555", -43); | |
| check_double(make_double(69116558615326153, -144) /* 3.0992936783063277e-027 */, "30992936783063277", -43); | |
| check_double(make_double(39462549494468513, -152) /* 6.9123512506176015e-030 */, "6912351250617602", -45); | |
| check_double(make_double(39462549494468513, -153) /* 3.4561756253088008e-030 */, "3456175625308801", -45); | |
| } | |
| } | |
| TEST_CASE("formatting") | |
| { | |
| SECTION("single precision") | |
| { | |
| auto check_float = [](float number, const std::string & expected) | |
| { | |
| std::array<char, 33> buf{}; | |
| char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) | |
| const std::string actual(buf.data(), end); | |
| CHECK(actual == expected); | |
| }; | |
| // %.9g | |
| check_float( -1.2345e-22f, "-1.2345e-22" ); // -1.23450004e-22 | |
| check_float( -1.2345e-21f, "-1.2345e-21" ); // -1.23450002e-21 | |
| check_float( -1.2345e-20f, "-1.2345e-20" ); // -1.23450002e-20 | |
| check_float( -1.2345e-19f, "-1.2345e-19" ); // -1.23449999e-19 | |
| check_float( -1.2345e-18f, "-1.2345e-18" ); // -1.23449996e-18 | |
| check_float( -1.2345e-17f, "-1.2345e-17" ); // -1.23449998e-17 | |
| check_float( -1.2345e-16f, "-1.2345e-16" ); // -1.23449996e-16 | |
| check_float( -1.2345e-15f, "-1.2345e-15" ); // -1.23450002e-15 | |
| check_float( -1.2345e-14f, "-1.2345e-14" ); // -1.23450004e-14 | |
| check_float( -1.2345e-13f, "-1.2345e-13" ); // -1.23449997e-13 | |
| check_float( -1.2345e-12f, "-1.2345e-12" ); // -1.23450002e-12 | |
| check_float( -1.2345e-11f, "-1.2345e-11" ); // -1.2345e-11 | |
| check_float( -1.2345e-10f, "-1.2345e-10" ); // -1.2345e-10 | |
| check_float( -1.2345e-9f, "-1.2345e-09" ); // -1.23449995e-09 | |
| check_float( -1.2345e-8f, "-1.2345e-08" ); // -1.23449997e-08 | |
| check_float( -1.2345e-7f, "-1.2345e-07" ); // -1.23449993e-07 | |
| check_float( -1.2345e-6f, "-1.2345e-06" ); // -1.23450002e-06 | |
| check_float( -1.2345e-5f, "-1.2345e-05" ); // -1.2345e-05 | |
| check_float( -1.2345e-4f, "-0.00012345" ); // -0.000123449994 | |
| check_float( -1.2345e-3f, "-0.0012345" ); // -0.00123449997 | |
| check_float( -1.2345e-2f, "-0.012345" ); // -0.0123450002 | |
| check_float( -1.2345e-1f, "-0.12345" ); // -0.123450004 | |
| check_float( -0.0f, "-0.0" ); // -0 | |
| check_float( 0.0f, "0.0" ); // 0 | |
| check_float( 1.2345e+0f, "1.2345" ); // 1.23450005 | |
| check_float( 1.2345e+1f, "12.345" ); // 12.3450003 | |
| check_float( 1.2345e+2f, "123.45" ); // 123.449997 | |
| check_float( 1.2345e+3f, "1234.5" ); // 1234.5 | |
| check_float( 1.2345e+4f, "12345.0" ); // 12345 | |
| check_float( 1.2345e+5f, "123450.0" ); // 123450 | |
| check_float( 1.2345e+6f, "1.2345e+06" ); // 1234500 | |
| check_float( 1.2345e+7f, "1.2345e+07" ); // 12345000 | |
| check_float( 1.2345e+8f, "1.2345e+08" ); // 123450000 | |
| check_float( 1.2345e+9f, "1.2345e+09" ); // 1.23449997e+09 | |
| check_float( 1.2345e+10f, "1.2345e+10" ); // 1.23449999e+10 | |
| check_float( 1.2345e+11f, "1.2345e+11" ); // 1.23449999e+11 | |
| check_float( 1.2345e+12f, "1.2345e+12" ); // 1.23450006e+12 | |
| check_float( 1.2345e+13f, "1.2345e+13" ); // 1.23449995e+13 | |
| check_float( 1.2345e+14f, "1.2345e+14" ); // 1.23450002e+14 | |
| check_float( 1.2345e+15f, "1.2345e+15" ); // 1.23450003e+15 | |
| check_float( 1.2345e+16f, "1.2345e+16" ); // 1.23449998e+16 | |
| check_float( 1.2345e+17f, "1.2345e+17" ); // 1.23449996e+17 | |
| check_float( 1.2345e+18f, "1.2345e+18" ); // 1.23450004e+18 | |
| check_float( 1.2345e+19f, "1.2345e+19" ); // 1.23449999e+19 | |
| check_float( 1.2345e+20f, "1.2345e+20" ); // 1.23449999e+20 | |
| check_float( 1.2345e+21f, "1.2345e+21" ); // 1.23449999e+21 | |
| check_float( 1.2345e+22f, "1.2345e+22" ); // 1.23450005e+22 | |
| } | |
| SECTION("double precision") | |
| { | |
| auto check_double = [](double number, const std::string & expected) | |
| { | |
| std::array<char, 33> buf{}; | |
| char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, number); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) | |
| const std::string actual(buf.data(), end); | |
| CHECK(actual == expected); | |
| }; | |
| // dtoa %.15g %.17g shortest | |
| check_double( -1.2345e-22, "-1.2345e-22" ); // -1.2345e-22 -1.2345000000000001e-22 -1.2345e-22 | |
| check_double( -1.2345e-21, "-1.2345e-21" ); // -1.2345e-21 -1.2345000000000001e-21 -1.2345e-21 | |
| check_double( -1.2345e-20, "-1.2345e-20" ); // -1.2345e-20 -1.2345e-20 -1.2345e-20 | |
| check_double( -1.2345e-19, "-1.2345e-19" ); // -1.2345e-19 -1.2345000000000001e-19 -1.2345e-19 | |
| check_double( -1.2345e-18, "-1.2345e-18" ); // -1.2345e-18 -1.2345000000000001e-18 -1.2345e-18 | |
| check_double( -1.2345e-17, "-1.2345e-17" ); // -1.2345e-17 -1.2345e-17 -1.2345e-17 | |
| check_double( -1.2345e-16, "-1.2345e-16" ); // -1.2345e-16 -1.2344999999999999e-16 -1.2345e-16 | |
| check_double( -1.2345e-15, "-1.2345e-15" ); // -1.2345e-15 -1.2345e-15 -1.2345e-15 | |
| check_double( -1.2345e-14, "-1.2345e-14" ); // -1.2345e-14 -1.2345e-14 -1.2345e-14 | |
| check_double( -1.2345e-13, "-1.2345e-13" ); // -1.2345e-13 -1.2344999999999999e-13 -1.2345e-13 | |
| check_double( -1.2345e-12, "-1.2345e-12" ); // -1.2345e-12 -1.2345e-12 -1.2345e-12 | |
| check_double( -1.2345e-11, "-1.2345e-11" ); // -1.2345e-11 -1.2345e-11 -1.2345e-11 | |
| check_double( -1.2345e-10, "-1.2345e-10" ); // -1.2345e-10 -1.2345e-10 -1.2345e-10 | |
| check_double( -1.2345e-9, "-1.2345e-09" ); // -1.2345e-09 -1.2345e-09 -1.2345e-9 | |
| check_double( -1.2345e-8, "-1.2345e-08" ); // -1.2345e-08 -1.2345000000000001e-08 -1.2345e-8 | |
| check_double( -1.2345e-7, "-1.2345e-07" ); // -1.2345e-07 -1.2345000000000001e-07 -1.2345e-7 | |
| check_double( -1.2345e-6, "-1.2345e-06" ); // -1.2345e-06 -1.2345e-06 -1.2345e-6 | |
| check_double( -1.2345e-5, "-1.2345e-05" ); // -1.2345e-05 -1.2345e-05 -1.2345e-5 | |
| check_double( -1.2345e-4, "-0.00012345" ); // -0.00012345 -0.00012344999999999999 -0.00012345 | |
| check_double( -1.2345e-3, "-0.0012345" ); // -0.0012345 -0.0012344999999999999 -0.0012345 | |
| check_double( -1.2345e-2, "-0.012345" ); // -0.012345 -0.012345 -0.012345 | |
| check_double( -1.2345e-1, "-0.12345" ); // -0.12345 -0.12345 -0.12345 | |
| check_double( -0.0, "-0.0" ); // -0 -0 -0 | |
| check_double( 0.0, "0.0" ); // 0 0 0 | |
| check_double( 1.2345e+0, "1.2345" ); // 1.2345 1.2344999999999999 1.2345 | |
| check_double( 1.2345e+1, "12.345" ); // 12.345 12.345000000000001 12.345 | |
| check_double( 1.2345e+2, "123.45" ); // 123.45 123.45 123.45 | |
| check_double( 1.2345e+3, "1234.5" ); // 1234.5 1234.5 1234.5 | |
| check_double( 1.2345e+4, "12345.0" ); // 12345 12345 12345 | |
| check_double( 1.2345e+5, "123450.0" ); // 123450 123450 123450 | |
| check_double( 1.2345e+6, "1234500.0" ); // 1234500 1234500 1234500 | |
| check_double( 1.2345e+7, "12345000.0" ); // 12345000 12345000 12345000 | |
| check_double( 1.2345e+8, "123450000.0" ); // 123450000 123450000 123450000 | |
| check_double( 1.2345e+9, "1234500000.0" ); // 1234500000 1234500000 1234500000 | |
| check_double( 1.2345e+10, "12345000000.0" ); // 12345000000 12345000000 12345000000 | |
| check_double( 1.2345e+11, "123450000000.0" ); // 123450000000 123450000000 123450000000 | |
| check_double( 1.2345e+12, "1234500000000.0" ); // 1234500000000 1234500000000 1234500000000 | |
| check_double( 1.2345e+13, "12345000000000.0" ); // 12345000000000 12345000000000 12345000000000 | |
| check_double( 1.2345e+14, "123450000000000.0" ); // 123450000000000 123450000000000 123450000000000 | |
| check_double( 1.2345e+15, "1.2345e+15" ); // 1.2345e+15 1234500000000000 1.2345e15 | |
| check_double( 1.2345e+16, "1.2345e+16" ); // 1.2345e+16 12345000000000000 1.2345e16 | |
| check_double( 1.2345e+17, "1.2345e+17" ); // 1.2345e+17 1.2345e+17 1.2345e17 | |
| check_double( 1.2345e+18, "1.2345e+18" ); // 1.2345e+18 1.2345e+18 1.2345e18 | |
| check_double( 1.2345e+19, "1.2345e+19" ); // 1.2345e+19 1.2345e+19 1.2345e19 | |
| check_double( 1.2345e+20, "1.2345e+20" ); // 1.2345e+20 1.2345e+20 1.2345e20 | |
| check_double( 1.2345e+21, "1.2345e+21" ); // 1.2345e+21 1.2344999999999999e+21 1.2345e21 | |
| check_double( 1.2345e+22, "1.2345e+22" ); // 1.2345e+22 1.2345e+22 1.2345e22 | |
| } | |
| SECTION("integer") | |
| { | |
| auto check_integer = [](std::int64_t number, const std::string & expected) | |
| { | |
| const nlohmann::json j = number; | |
| CHECK(j.dump() == expected); | |
| }; | |
| // edge cases | |
| check_integer(INT64_MIN, "-9223372036854775808"); | |
| check_integer(INT64_MAX, "9223372036854775807"); | |
| // few random big integers | |
| check_integer(-3456789012345678901LL, "-3456789012345678901"); | |
| check_integer(3456789012345678901LL, "3456789012345678901"); | |
| check_integer(-5678901234567890123LL, "-5678901234567890123"); | |
| check_integer(5678901234567890123LL, "5678901234567890123"); | |
| // integers with various digit counts | |
| check_integer(-1000000000000000000LL, "-1000000000000000000"); | |
| check_integer(-100000000000000000LL, "-100000000000000000"); | |
| check_integer(-10000000000000000LL, "-10000000000000000"); | |
| check_integer(-1000000000000000LL, "-1000000000000000"); | |
| check_integer(-100000000000000LL, "-100000000000000"); | |
| check_integer(-10000000000000LL, "-10000000000000"); | |
| check_integer(-1000000000000LL, "-1000000000000"); | |
| check_integer(-100000000000LL, "-100000000000"); | |
| check_integer(-10000000000LL, "-10000000000"); | |
| check_integer(-1000000000LL, "-1000000000"); | |
| check_integer(-100000000LL, "-100000000"); | |
| check_integer(-10000000LL, "-10000000"); | |
| check_integer(-1000000LL, "-1000000"); | |
| check_integer(-100000LL, "-100000"); | |
| check_integer(-10000LL, "-10000"); | |
| check_integer(-1000LL, "-1000"); | |
| check_integer(-100LL, "-100"); | |
| check_integer(-10LL, "-10"); | |
| check_integer(-1LL, "-1"); | |
| check_integer(0, "0"); | |
| check_integer(1LL, "1"); | |
| check_integer(10LL, "10"); | |
| check_integer(100LL, "100"); | |
| check_integer(1000LL, "1000"); | |
| check_integer(10000LL, "10000"); | |
| check_integer(100000LL, "100000"); | |
| check_integer(1000000LL, "1000000"); | |
| check_integer(10000000LL, "10000000"); | |
| check_integer(100000000LL, "100000000"); | |
| check_integer(1000000000LL, "1000000000"); | |
| check_integer(10000000000LL, "10000000000"); | |
| check_integer(100000000000LL, "100000000000"); | |
| check_integer(1000000000000LL, "1000000000000"); | |
| check_integer(10000000000000LL, "10000000000000"); | |
| check_integer(100000000000000LL, "100000000000000"); | |
| check_integer(1000000000000000LL, "1000000000000000"); | |
| check_integer(10000000000000000LL, "10000000000000000"); | |
| check_integer(100000000000000000LL, "100000000000000000"); | |
| check_integer(1000000000000000000LL, "1000000000000000000"); | |
| } | |
| } | |
| namespace | |
| { | |
| // a small unsigned big integer (32-bit limbs, least significant first), to | |
| // recompute the powers of ten of the shortest double conversion | |
| using big = std::vector<std::uint32_t>; | |
| void big_mul_small(big& x, std::uint32_t m) | |
| { | |
| std::uint64_t carry = 0; | |
| for (auto& limb : x) | |
| { | |
| const std::uint64_t v = (static_cast<std::uint64_t>(limb) * m) + carry; | |
| limb = static_cast<std::uint32_t>(v); | |
| carry = v >> 32u; | |
| } | |
| if (carry != 0) | |
| { | |
| x.push_back(static_cast<std::uint32_t>(carry)); | |
| } | |
| } | |
| void big_div_small(big& x, std::uint32_t d) | |
| { | |
| std::uint64_t rest = 0; | |
| for (std::size_t i = x.size(); i-- > 0;) | |
| { | |
| const std::uint64_t v = (rest << 32u) | x[i]; | |
| x[i] = static_cast<std::uint32_t>(v / d); | |
| rest = v % d; | |
| } | |
| while (!x.empty() && x.back() == 0) | |
| { | |
| x.pop_back(); | |
| } | |
| } | |
| std::size_t big_bit_length(const big& x) | |
| { | |
| std::size_t n = 32 * x.size(); | |
| for (std::uint32_t top = x.back(); (top & 0x80000000u) == 0; top <<= 1u) | |
| { | |
| --n; | |
| } | |
| return n; | |
| } | |
| bool big_bit(const big& x, std::size_t i) | |
| { | |
| return ((x[i / 32] >> (i % 32)) & 1u) != 0; | |
| } | |
| /// the 128 most significant bits of x (floor), shifted left if x has fewer bits | |
| std::pair<std::uint64_t, std::uint64_t> big_top128(const big& x) | |
| { | |
| const std::size_t n = big_bit_length(x); | |
| std::uint64_t high = 0; | |
| std::uint64_t low = 0; | |
| for (std::size_t k = 0; k < 128; ++k) | |
| { | |
| const bool bit = k < n && big_bit(x, n - 1 - k); | |
| if (k < 64) | |
| { | |
| high = (high << 1u) | (bit ? 1u : 0u); | |
| } | |
| else | |
| { | |
| low = (low << 1u) | (bit ? 1u : 0u); | |
| } | |
| } | |
| return {high, low}; | |
| } | |
| /// the digits (without trailing zeros) and the decimal exponent of a | |
| /// representation "[-]d[.ddd][e[+-]x]" | |
| std::pair<std::string, int> digits_and_exponent(const std::string& s) | |
| { | |
| std::string digits; | |
| int point = -1; | |
| int exponent = 0; | |
| for (std::size_t i = 0; i < s.size(); ++i) | |
| { | |
| const char c = s[i]; | |
| if (c >= '0' && c <= '9') | |
| { | |
| digits += c; | |
| } | |
| else if (c == '.') | |
| { | |
| point = static_cast<int>(digits.size()); | |
| } | |
| else if (c == 'e' || c == 'E') | |
| { | |
| exponent = std::stoi(s.substr(i + 1)); | |
| break; | |
| } | |
| } | |
| int e = exponent + (point < 0 ? static_cast<int>(digits.size()) : point) - static_cast<int>(digits.size()); | |
| const std::size_t first = digits.find_first_not_of('0'); | |
| digits = first == std::string::npos ? "0" : digits.substr(first); | |
| while (digits.size() > 1 && digits.back() == '0') | |
| { | |
| digits.pop_back(); | |
| ++e; | |
| } | |
| return {digits, e}; | |
| } | |
| /// the correctly rounded double of a decimal text | |
| /// (not std::strtod: the C runtimes of some platforms, e.g. MinGW's, round | |
| /// some 16 and 17 digit inputs wrongly) | |
| double parse_double(const std::string& text) | |
| { | |
| const nlohmann::json j = nlohmann::json::parse(text, nullptr, false); | |
| // (a discarded value: out of range, as strtod's HUGE_VAL) | |
| return j.is_discarded() ? std::numeric_limits<double>::infinity() : j.get<double>(); | |
| } | |
| /// whether the decimal digits * 10^e reads back as v | |
| bool reads_back(const std::string& digits, int e, double v) | |
| { | |
| const std::string text = digits + "e" + std::to_string(e); | |
| // (compared bit for bit: v is positive and finite, and -Wfloat-equal) | |
| return bit_cast<std::uint64_t>(parse_double(text)) == bit_cast<std::uint64_t>(v); | |
| } | |
| /// Check the representation of a positive finite double: it reads back as | |
| /// the same value, and no representation with fewer digits does. | |
| void check_shortest(double v) | |
| { | |
| std::array<char, 33> buf{}; | |
| char* end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, v); | |
| const std::string text(buf.data(), end); | |
| CAPTURE(text) | |
| CHECK(parse_double(text) == v); | |
| // the layout is that of format_buffer() for the digits of Zmij | |
| const auto sd = nlohmann::detail::zmij::to_shortest(bit_cast<std::uint64_t>(v)); | |
| const std::uint64_t significand = sd.has_digit ? (sd.integral * 10) + sd.digit : sd.integral; | |
| int exponent = sd.has_digit ? sd.exponent : sd.exponent + 1; | |
| std::string significand_digits = std::to_string(significand); | |
| while (significand_digits.size() > 1 && significand_digits.back() == '0') | |
| { | |
| significand_digits.pop_back(); | |
| ++exponent; | |
| } | |
| std::array<char, 64> reference{}; | |
| std::copy(significand_digits.begin(), significand_digits.end(), reference.begin()); | |
| const char* const reference_end = nlohmann::detail::dtoa_impl::format_buffer(reference.data(), static_cast<int>(significand_digits.size()), exponent, -4, 15); | |
| CHECK(text == std::string(reference.data(), static_cast<std::size_t>(reference_end - reference.data()))); | |
| // and write_positive() is what to_chars() calls | |
| std::array<char, 64> positive{}; | |
| const char* const positive_end = nlohmann::detail::dtoa_impl::write_positive(positive.data(), positive.data() + positive.size(), v); | |
| CHECK(text == std::string(positive.data(), static_cast<std::size_t>(positive_end - positive.data()))); | |
| const auto de = digits_and_exponent(text); | |
| const std::string& digits = de.first; | |
| if (digits.size() > 1) | |
| { | |
| // the decimals of one digit fewer next to the value: v lies within | |
| // half a unit of the last digit of text, so the closest ones are the | |
| // truncated digits and their successor | |
| // (not a stream with std::setprecision(digits.size() - 2): the | |
| // standard library of MSVC 2017 ignores a precision of 0) | |
| const std::string m = digits.substr(0, digits.size() - 1); | |
| const int e = de.second + 1; | |
| const std::uint64_t mid = std::stoull(m); | |
| for (const std::uint64_t candidate : | |
| { | |
| mid - 1, mid, mid + 1 | |
| }) | |
| { | |
| CAPTURE(candidate) | |
| CHECK(!reads_back(std::to_string(candidate), e, v)); | |
| } | |
| } | |
| // (icpc with libstdc++ 11 defines __cpp_lib_to_chars, but has no floating-point std::to_chars) | |
| #if defined(JSON_HAS_CPP_17) && defined(__cpp_lib_to_chars) && !defined(__INTEL_COMPILER) | |
| // the closest of the shortest representations, as std::to_chars finds it | |
| std::array<char, 64> std_text{}; | |
| const auto r = std::to_chars(std_text.data(), std_text.data() + std_text.size(), v, std::chars_format::scientific); | |
| CHECK(digits_and_exponent(std::string(std_text.data(), r.ptr)) == de); | |
| #endif | |
| } | |
| } // namespace | |
| TEST_CASE("shortest digits of doubles") | |
| { | |
| SECTION("powers of ten") | |
| { | |
| // the 128-bit significands of 10^k, rounded down, recomputed | |
| for (int k = -342; k <= 341; ++k) | |
| { | |
| CAPTURE(k) | |
| big x{1}; | |
| if (k >= 0) | |
| { | |
| for (int i = 0; i < k; ++i) | |
| { | |
| big_mul_small(x, 10); | |
| } | |
| } | |
| else | |
| { | |
| // floor(2^b / 10^-k) for a b that leaves more than 128 bits | |
| const int b = 128 + 64 + (4 * -k); | |
| x.assign(static_cast<std::size_t>(b / 32) + 1, 0); | |
| x.back() = 1u << (b % 32); | |
| for (int i = 0; i < -k; ++i) | |
| { | |
| big_div_small(x, 10); | |
| } | |
| } | |
| const auto expected = big_top128(x); | |
| const auto actual = nlohmann::detail::zmij::pow10(k); | |
| CHECK(actual.high == expected.first); | |
| CHECK(actual.low == expected.second); | |
| } | |
| } | |
| SECTION("boundary values") | |
| { | |
| for (const double v : | |
| { | |
| std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), std::numeric_limits<double>::denorm_min(), | |
| std::nextafter(std::numeric_limits<double>::min(), 0.0), 1.0, 2.0, 0.1, 0.3, 1e21, 1e22, 1e23, 5e-324, 9007199254740993.0, | |
| 1.2345e+21, 2.2250738585072014e-308, 1.7976931348623157e308, 4.9406564584124654e-324, 123456789012345680.0 | |
| }) | |
| { | |
| check_shortest(v); | |
| } | |
| // all powers of two (their rounding interval is narrower below) | |
| for (int e = -1074; e <= 1023; ++e) | |
| { | |
| check_shortest(std::ldexp(1.0, e)); | |
| } | |
| // powers of ten and their neighbors | |
| for (int e = -323; e <= 308; ++e) | |
| { | |
| const double p = std::strtod(("1e" + std::to_string(e)).c_str(), nullptr); | |
| check_shortest(p); | |
| check_shortest(std::nextafter(p, 0.0)); | |
| check_shortest(std::nextafter(p, std::numeric_limits<double>::infinity())); | |
| } | |
| } | |
| SECTION("random doubles") | |
| { | |
| std::mt19937_64 rng(5295); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed): reproducible | |
| for (int i = 0; i < 100000; ++i) | |
| { | |
| const std::uint64_t bits = rng() & 0x7FFFFFFFFFFFFFFFu; | |
| const auto v = bit_cast<double>(bits); | |
| if (std::isfinite(v) && bits != 0) | |
| { | |
| check_shortest(v); | |
| } | |
| } | |
| } | |
| } | |
| TEST_CASE("choice of the conversion") | |
| { | |
| using nlohmann::detail::dtoa_impl::is_binary64; | |
| SECTION("by the format of the type") | |
| { | |
| // Zmij needs binary64 numbers; everything else uses Grisu2 | |
| static_assert(!is_binary64<float>::value, "float is not binary64"); | |
| static_assert(is_binary64<double>::value == (std::numeric_limits<double>::is_iec559 && std::numeric_limits<double>::digits == 53), | |
| "double is binary64 where it is IEEE 754 with 53 digits"); | |
| static_assert(!is_binary64<int>::value, "integers are not binary64"); | |
| static_assert(is_binary64<long double>::value == (std::numeric_limits<long double>::is_iec559 && std::numeric_limits<long double>::digits == 53 && sizeof(long double) == 8), | |
| "long double is binary64 where it has the format of a double"); | |
| CHECK(!is_binary64<float>::value); | |
| CHECK(is_binary64<double>::value); | |
| } | |
| SECTION("float: Grisu2, double: Zmij") | |
| { | |
| // 5.3165205877497296e+16 is one of the doubles for which Grisu2 does not find the shortest digits | |
| constexpr double value = 5.3165205877497296e+16; | |
| std::array<char, 64> buf{}; | |
| const char* const last = buf.data() + buf.size(); | |
| char* end = nlohmann::detail::dtoa_impl::write_positive(buf.data(), last, value); | |
| CHECK(std::string(buf.data(), end) == "5.31652058774973e+16"); | |
| end = nlohmann::detail::dtoa_impl::write_positive_grisu2(buf.data(), last, value); | |
| CHECK(std::string(buf.data(), end) == "5.3165205877497296e+16"); | |
| constexpr float f = 1.1754944e-38f; | |
| end = nlohmann::detail::dtoa_impl::write_positive(buf.data(), last, f); | |
| const std::string dispatched(buf.data(), end); | |
| end = nlohmann::detail::dtoa_impl::write_positive_grisu2(buf.data(), last, f); | |
| CHECK(dispatched == std::string(buf.data(), end)); | |
| } | |
| SECTION("long double with the format of a double: Zmij") | |
| { | |
| // (on platforms where long double is wider, Grisu2 does not apply either: the snprintf fallback does) | |
| // (constant on a given platform) | |
| DOCTEST_MSVC_SUPPRESS_WARNING_PUSH | |
| DOCTEST_MSVC_SUPPRESS_WARNING(4127) | |
| if (std::numeric_limits<long double>::digits == 53 && std::numeric_limits<long double>::is_iec559) | |
| { | |
| using long_double_json = nlohmann::json::with_float_t<long double>; | |
| for (const double d : | |
| { | |
| 5.3165205877497296e+16, 1.0, 0.1, 123456.789, 2.2250738585072014e-308, 1.7976931348623157e+308, -5.3165205877497296e+16 | |
| }) | |
| { | |
| CAPTURE(d) | |
| CHECK(long_double_json(static_cast<long double>(d)).dump() == nlohmann::json(d).dump()); | |
| } | |
| CHECK(long_double_json(5.3165205877497296e+16L).dump() == "5.31652058774973e+16"); | |
| } | |
| DOCTEST_MSVC_SUPPRESS_WARNING_POP | |
| } | |
| } | |
| TEST_CASE("short decimals") | |
| { | |
| // write_short_decimal() writes digits * 10^exp for the digits of a double | |
| // that need no conversion (at most 15, the first not 0): as to_chars() | |
| // writes the (positive) double that has these digits | |
| const auto written = [](std::uint64_t digits, int exp) -> std::string | |
| { | |
| std::array<char, 64> buf{}; // (up to 41 bytes are written) | |
| char* const end = nlohmann::detail::dtoa_impl::write_short_decimal(buf.data(), digits, exp); | |
| return {buf.data(), end}; | |
| }; | |
| const auto written_counted = [](std::uint64_t digits, int count, int exp) -> std::string | |
| { | |
| std::array<char, 64> buf{}; | |
| char* const end = nlohmann::detail::dtoa_impl::write_short_decimal(buf.data(), digits, count, exp); | |
| return {buf.data(), end}; | |
| }; | |
| const auto expected = [](std::uint64_t digits, int exp) -> std::string | |
| { | |
| const double value = std::strtod((std::to_string(digits) + "e" + std::to_string(exp)).c_str(), nullptr); | |
| std::array<char, 64> buf{}; | |
| char* const end = nlohmann::detail::to_chars(buf.data(), buf.data() + 32, value); | |
| return {buf.data(), end}; | |
| }; | |
| SECTION("powers of ten") | |
| { | |
| const auto& powers = nlohmann::detail::dtoa_impl::powers_of_ten(); | |
| CHECK(powers.front() == 1u); | |
| for (std::size_t i = 1; i < powers.size(); ++i) | |
| { | |
| CHECK(powers[i] == powers[i - 1] * 10u); | |
| } | |
| CHECK(powers.back() == 10000000000000000000u); | |
| } | |
| SECTION("examples") | |
| { | |
| CHECK(written(1, 0) == "1.0"); | |
| CHECK(written(15, -1) == "1.5"); | |
| CHECK(written(125, -2) == "1.25"); | |
| CHECK(written(1, 22) == "1e+22"); | |
| CHECK(written(123456789012345, -2) == "1234567890123.45"); | |
| CHECK(written(999999999999999, -15) == "0.999999999999999"); | |
| CHECK(written(5, -324) == "5e-324"); | |
| CHECK(written_counted(1, 1, 0) == "1.0"); | |
| CHECK(written_counted(125, 3, -2) == "1.25"); | |
| CHECK(written_counted(100, 3, -2) == "1.0"); | |
| CHECK(written_counted(999999999999999, 15, -15) == "0.999999999999999"); | |
| } | |
| SECTION("random digits, exponents and trailing zeros") | |
| { | |
| std::mt19937_64 rng(1170); // NOLINT(cert-msc32-c,cert-msc51-cpp,bugprone-random-generator-seed) | |
| for (int i = 0; i < 100000; ++i) | |
| { | |
| // 1 to 15 digits, the first not 0, and up to 14 of them trailing zeros | |
| std::uint64_t count = 1 + (rng() % 15u); | |
| std::uint64_t power = 1; | |
| for (std::uint64_t k = 1; k < count; ++k) | |
| { | |
| power *= 10; | |
| } | |
| std::uint64_t digits = power + (rng() % (9 * power)); | |
| const std::uint64_t zeros = (rng() % 3u == 0) ? (rng() % count) : 0; | |
| for (std::uint64_t k = 0; k < zeros; ++k) | |
| { | |
| digits = (digits / 10) * 10; | |
| } | |
| // (a value between 1e-300 and 1e300) | |
| const int exp = static_cast<int>(rng() % 560u) - 300 - static_cast<int>(count); | |
| CAPTURE(digits) | |
| CAPTURE(count) | |
| CAPTURE(exp) | |
| const std::string want = expected(digits, exp); | |
| CHECK(written(digits, exp) == want); | |
| CHECK(written_counted(digits, static_cast<int>(count), exp) == want); | |
| } | |
| } | |
| } |