From 6130f2ea1a61f7408201132045bc50f494763d10 Mon Sep 17 00:00:00 2001 From: June Han Date: Wed, 25 Oct 2023 14:16:38 +0900 Subject: [PATCH 1/7] fix: minimize the precision loss when dumping double to string --- include/crow/json.h | 5 +++-- tests/unittest.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index 3359a9cc0..357f16c0a 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1851,10 +1851,11 @@ namespace crow } f_state; char outbuf[128]; #ifdef _MSC_VER - sprintf_s(outbuf, sizeof(outbuf), "%f", v.num.d); +#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf_s((BUFFER_PTR), 128, (FORMAT_PTR), DBL_DECIMAL_DIG, (VALUE)) #else - snprintf(outbuf, sizeof(outbuf), "%f", v.num.d); +#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf((BUFFER_PTR), (FORMAT_PTR), DECIMAL_DIG, (VALUE)) #endif + MSC_COMPATIBLE_SPRINTF(outbuf, "%.*g", v.num.d); char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0 f_state = start; while (*p != '\0') diff --git a/tests/unittest.cpp b/tests/unittest.cpp index b280f07d6..06345cecc 100644 --- a/tests/unittest.cpp +++ b/tests/unittest.cpp @@ -1038,11 +1038,13 @@ TEST_CASE("json::wvalue::wvalue(float)") TEST_CASE("json::wvalue::wvalue(double)") { - double d = 4.2; + double d = 0.036303908355795146; json::wvalue value = d; CHECK(value.t() == json::type::Number); - CHECK(value.dump() == "4.2"); + auto dumped_value = value.dump(); + CROW_LOG_DEBUG << dumped_value; + CHECK(std::abs(utility::lexical_cast(dumped_value) - d) < numeric_limits::epsilon()); } // json::wvalue::wvalue(double) TEST_CASE("json::wvalue::wvalue(char const*)") From 8bb7cf15b520e06bd63f23b0ceb641e149480699 Mon Sep 17 00:00:00 2001 From: June Han Date: Sun, 21 Jan 2024 22:22:34 +0900 Subject: [PATCH 2/7] fix: abandon one-time macro --- include/crow/json.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index 357f16c0a..0e023b836 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1851,11 +1851,10 @@ namespace crow } f_state; char outbuf[128]; #ifdef _MSC_VER -#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf_s((BUFFER_PTR), 128, (FORMAT_PTR), DBL_DECIMAL_DIG, (VALUE)) + sprintf_s(outbuf, 128, "%.*g", DBL_DECIMAL_DIG, v.num.d); #else -#define MSC_COMPATIBLE_SPRINTF(BUFFER_PTR, FORMAT_PTR, VALUE) sprintf((BUFFER_PTR), (FORMAT_PTR), DECIMAL_DIG, (VALUE)) + sprintf(outbuf, "%.*g", DECIMAL_DIG, v.num.d); #endif - MSC_COMPATIBLE_SPRINTF(outbuf, "%.*g", v.num.d); char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0 f_state = start; while (*p != '\0') From 543fa7a7e1ba95e5abb821877ae054ac27168e8f Mon Sep 17 00:00:00 2001 From: June Han Date: Mon, 22 Jan 2024 23:02:05 +0900 Subject: [PATCH 3/7] fix: replace hard-coded buffer size, replace `sprintf` with `snprintf` --- include/crow/json.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index 0e023b836..51ae7ff18 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1851,9 +1851,9 @@ namespace crow } f_state; char outbuf[128]; #ifdef _MSC_VER - sprintf_s(outbuf, 128, "%.*g", DBL_DECIMAL_DIG, v.num.d); + sprintf_s(outbuf, sizeof(outbuf), "%.*g", DBL_DECIMAL_DIG, v.num.d); #else - sprintf(outbuf, "%.*g", DECIMAL_DIG, v.num.d); + snprintf(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); #endif char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0 f_state = start; From f1894c57ec478a393d4b788ae02aba3a670b34c6 Mon Sep 17 00:00:00 2001 From: June Han Date: Tue, 23 Jan 2024 09:24:45 +0900 Subject: [PATCH 4/7] fix: replace `DECIMAL_DIG` with `DBL_DECIMAL_DIG` --- include/crow/json.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/crow/json.h b/include/crow/json.h index 51ae7ff18..ebc46a860 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1853,7 +1853,7 @@ namespace crow #ifdef _MSC_VER sprintf_s(outbuf, sizeof(outbuf), "%.*g", DBL_DECIMAL_DIG, v.num.d); #else - snprintf(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); + snprintf(outbuf, sizeof(outbuf), "%.*g", DBL_DECIMAL_DIG, v.num.d); #endif char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0 f_state = start; From 75d7811e9f91a4a1d62437c97f45ebbaa8d41c33 Mon Sep 17 00:00:00 2001 From: gittiver Date: Tue, 23 Jan 2024 12:47:48 +0100 Subject: [PATCH 5/7] Update json.h changed to C++11 DECIMAL_DIGIT --- include/crow/json.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index ebc46a860..560c3440e 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -1851,9 +1851,9 @@ namespace crow } f_state; char outbuf[128]; #ifdef _MSC_VER - sprintf_s(outbuf, sizeof(outbuf), "%.*g", DBL_DECIMAL_DIG, v.num.d); + sprintf_s(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); #else - snprintf(outbuf, sizeof(outbuf), "%.*g", DBL_DECIMAL_DIG, v.num.d); + snprintf(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); #endif char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0 f_state = start; From 69b17ccdc1e6d845c528751685a5fb3c29c098c9 Mon Sep 17 00:00:00 2001 From: gittiver Date: Tue, 23 Jan 2024 12:50:46 +0100 Subject: [PATCH 6/7] added cfloat include --- include/crow/json.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/crow/json.h b/include/crow/json.h index 560c3440e..7240aedc1 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -14,6 +14,7 @@ #include #include #include +#include #include "crow/utility.h" #include "crow/settings.h" From 5ba4b39adb3354ce88480ba1f69761e0a52260bf Mon Sep 17 00:00:00 2001 From: June Han Date: Wed, 24 Jan 2024 12:51:12 +0900 Subject: [PATCH 7/7] fix: identify float and double as different numeral types, and approach their precisions accordingly --- include/crow/json.h | 48 +++++++++++++++++++++++------- include/crow/middlewares/session.h | 2 +- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/include/crow/json.h b/include/crow/json.h index 7240aedc1..8d067295b 100644 --- a/include/crow/json.h +++ b/include/crow/json.h @@ -106,7 +106,8 @@ namespace crow Signed_integer, Unsigned_integer, Floating_point, - Null + Null, + Double_precision_floating_point }; class rvalue; @@ -782,6 +783,7 @@ namespace crow switch (r.nt()) { case num_type::Floating_point: os << r.d(); break; + case num_type::Double_precision_floating_point: os << r.d(); break; case num_type::Signed_integer: os << r.i(); break; case num_type::Unsigned_integer: os << r.u(); break; case num_type::Null: throw std::runtime_error("Number with num_type Null"); @@ -1319,7 +1321,9 @@ namespace crow ui(value) {} constexpr number(std::int64_t value) noexcept: si(value) {} - constexpr number(double value) noexcept: + explicit constexpr number(double value) noexcept: + d(value) {} + explicit constexpr number(float value) noexcept: d(value) {} } num; ///< Value if type is a number. std::string s; ///< Value if type is a string. @@ -1358,7 +1362,7 @@ namespace crow wvalue(float value): returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast(value)) {} wvalue(double value): - returnable("application/json"), t_(type::Number), nt(num_type::Floating_point), num(static_cast(value)) {} + returnable("application/json"), t_(type::Number), nt(num_type::Double_precision_floating_point), num(static_cast(value)) {} wvalue(char const* value): returnable("application/json"), t_(type::String), s(value) {} @@ -1409,7 +1413,7 @@ namespace crow return; case type::Number: nt = r.nt(); - if (nt == num_type::Floating_point) + if (nt == num_type::Floating_point || nt == num_type::Double_precision_floating_point) num.d = r.d(); else if (nt == num_type::Signed_integer) num.si = r.i(); @@ -1445,7 +1449,7 @@ namespace crow return; case type::Number: nt = r.nt; - if (nt == num_type::Floating_point) + if (nt == num_type::Floating_point || nt == num_type::Double_precision_floating_point) num.d = r.num.d; else if (nt == num_type::Signed_integer) num.si = r.num.si; @@ -1515,7 +1519,7 @@ namespace crow return *this; } - wvalue& operator=(double value) + wvalue& operator=(float value) { reset(); t_ = type::Number; @@ -1524,6 +1528,15 @@ namespace crow return *this; } + wvalue& operator=(double value) + { + reset(); + t_ = type::Number; + num.d = value; + nt = num_type::Double_precision_floating_point; + return *this; + } + wvalue& operator=(unsigned short value) { reset(); @@ -1836,7 +1849,7 @@ namespace crow case type::True: out += "true"; break; case type::Number: { - if (v.nt == num_type::Floating_point) + if (v.nt == num_type::Floating_point || v.nt == num_type::Double_precision_floating_point) { if (isnan(v.num.d) || isinf(v.num.d)) { @@ -1851,11 +1864,22 @@ namespace crow zero } f_state; char outbuf[128]; + if (v.nt == num_type::Double_precision_floating_point) + { #ifdef _MSC_VER - sprintf_s(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); + sprintf_s(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); #else - snprintf(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); + snprintf(outbuf, sizeof(outbuf), "%.*g", DECIMAL_DIG, v.num.d); #endif + } + else + { +#ifdef _MSC_VER + sprintf_s(outbuf, sizeof(outbuf), "%f", v.num.d); +#else + snprintf(outbuf, sizeof(outbuf), "%f", v.num.d); +#endif + } char *p = &outbuf[0], *o = nullptr; // o is the position of the first trailing 0 f_state = start; while (*p != '\0') @@ -1969,14 +1993,16 @@ namespace crow { int64_t get(int64_t fallback) { - if (ref.t() != type::Number || ref.nt == num_type::Floating_point) + if (ref.t() != type::Number || ref.nt == num_type::Floating_point || + ref.nt == num_type::Double_precision_floating_point) return fallback; return ref.num.si; } double get(double fallback) { - if (ref.t() != type::Number || ref.nt != num_type::Floating_point) + if (ref.t() != type::Number || ref.nt != num_type::Floating_point || + ref.nt == num_type::Double_precision_floating_point) return fallback; return ref.num.d; } diff --git a/include/crow/middlewares/session.h b/include/crow/middlewares/session.h index 8715c7943..66fe4c412 100644 --- a/include/crow/middlewares/session.h +++ b/include/crow/middlewares/session.h @@ -105,7 +105,7 @@ namespace crow { case type::Number: { - if (rv.nt() == num_type::Floating_point) + if (rv.nt() == num_type::Floating_point || rv.nt() == num_type::Double_precision_floating_point) return multi_value{rv.d()}; else if (rv.nt() == num_type::Unsigned_integer) return multi_value{int64_t(rv.u())};