From 09a905ca8066c86951b23023a9e1950f277ae8f4 Mon Sep 17 00:00:00 2001 From: Heikki Simojoki Date: Mon, 23 Dec 2019 17:38:33 +0200 Subject: [PATCH] Increase String::num default decimal precision Fixes #34541 Renamed MAX_DIGITS to MAX_DECIMALS, since it only changes the amount of digits after the decimal point. Increased MAX_DECIMALS to 32, and made String::num use MAX_DECIMALS consistently. If -1 is passed as decimal precision to String::num, it now gets changed to the correct precision based on the number's magnitude, instead of using printf default(which is 6) String::num_real also calculates the correct precision now. Also made the types used in floating-point math more consistent in a few places. --- core/core_bind.cpp | 6 ++--- core/core_bind.h | 2 +- core/io/json.cpp | 19 +++++++++---- core/io/json.h | 4 +-- core/string/ustring.cpp | 31 +++++++++++++++------- doc/classes/JSON.xml | 3 +++ modules/gdscript/doc_classes/@GDScript.xml | 4 +-- 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index b8c448dc82..c3e4066c4b 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -2415,12 +2415,12 @@ Variant JSONParseResult::get_result() const { } void _JSON::_bind_methods() { - ClassDB::bind_method(D_METHOD("print", "value", "indent", "sort_keys"), &_JSON::print, DEFVAL(String()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("print", "value", "indent", "sort_keys", "full_precision"), &_JSON::print, DEFVAL(String()), DEFVAL(false), DEFVAL(false)); ClassDB::bind_method(D_METHOD("parse", "json"), &_JSON::parse); } -String _JSON::print(const Variant &p_value, const String &p_indent, bool p_sort_keys) { - return JSON::print(p_value, p_indent, p_sort_keys); +String _JSON::print(const Variant &p_value, const String &p_indent, bool p_sort_keys, bool p_full_precision) { + return JSON::print(p_value, p_indent, p_sort_keys, p_full_precision); } Ref _JSON::parse(const String &p_json) { diff --git a/core/core_bind.h b/core/core_bind.h index be8b30b38d..3c63304488 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -705,7 +705,7 @@ protected: public: static _JSON *get_singleton() { return singleton; } - String print(const Variant &p_value, const String &p_indent = "", bool p_sort_keys = false); + String print(const Variant &p_value, const String &p_indent = "", bool p_sort_keys = false, bool p_full_precision = false); Ref parse(const String &p_json); _JSON() { singleton = this; } diff --git a/core/io/json.cpp b/core/io/json.cpp index 394cf216e8..e3e9d6158b 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -55,7 +55,7 @@ static String _make_indent(const String &p_indent, int p_size) { return indent_text; } -String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys) { +String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, bool p_full_precision) { String colon = ":"; String end_statement = ""; @@ -71,8 +71,17 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ return p_var.operator bool() ? "true" : "false"; case Variant::INT: return itos(p_var); - case Variant::FLOAT: - return rtos(p_var); + case Variant::FLOAT: { + double num = p_var; + if (p_full_precision) { + // Store unreliable digits (17) instead of just reliable + // digits (14) so that the value can be decoded exactly. + return String::num(num, 17 - (int)floor(log10(num))); + } else { + // Store only reliable digits (14) by default. + return String::num(num, 14 - (int)floor(log10(num))); + } + } case Variant::PACKED_INT32_ARRAY: case Variant::PACKED_INT64_ARRAY: case Variant::PACKED_FLOAT32_ARRAY: @@ -121,8 +130,8 @@ String JSON::_print_var(const Variant &p_var, const String &p_indent, int p_cur_ } } -String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys) { - return _print_var(p_var, p_indent, 0, p_sort_keys); +String JSON::print(const Variant &p_var, const String &p_indent, bool p_sort_keys, bool p_full_precision) { + return _print_var(p_var, p_indent, 0, p_sort_keys, p_full_precision); } Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str) { diff --git a/core/io/json.h b/core/io/json.h index 537477666e..f2711d8c54 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -62,7 +62,7 @@ class JSON { static const char *tk_name[TK_MAX]; - static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys); + static String _print_var(const Variant &p_var, const String &p_indent, int p_cur_indent, bool p_sort_keys, bool p_full_precision = false); static Error _get_token(const char32_t *p_str, int &index, int p_len, Token &r_token, int &line, String &r_err_str); static Error _parse_value(Variant &value, Token &token, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); @@ -70,7 +70,7 @@ class JSON { static Error _parse_object(Dictionary &object, const char32_t *p_str, int &index, int p_len, int &line, String &r_err_str); public: - static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true); + static String print(const Variant &p_var, const String &p_indent = "", bool p_sort_keys = true, bool p_full_precision = false); static Error parse(const String &p_json, Variant &r_ret, String &r_err_str, int &r_err_line); }; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 49cf171f2b..6a19baa94b 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -54,7 +54,7 @@ #define snprintf _snprintf_s #endif -#define MAX_DIGITS 6 +#define MAX_DECIMALS 32 #define UPPERCASE(m_c) (((m_c) >= 'a' && (m_c) <= 'z') ? ((m_c) - ('a' - 'A')) : (m_c)) #define LOWERCASE(m_c) (((m_c) >= 'A' && (m_c) <= 'Z') ? ((m_c) + ('a' - 'A')) : (m_c)) #define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9') @@ -1379,8 +1379,11 @@ String String::num(double p_num, int p_decimals) { } #ifndef NO_USE_STDLIB - if (p_decimals > 16) { - p_decimals = 16; + if (p_decimals < 0) { + p_decimals = 14 - (int)floor(log10(p_num)); + } + if (p_decimals > MAX_DECIMALS) { + p_decimals = MAX_DECIMALS; } char fmt[7]; @@ -1391,7 +1394,6 @@ String String::num(double p_num, int p_decimals) { fmt[1] = 'l'; fmt[2] = 'f'; fmt[3] = 0; - } else if (p_decimals < 10) { fmt[2] = '0' + p_decimals; fmt[3] = 'l'; @@ -1458,8 +1460,9 @@ String String::num(double p_num, int p_decimals) { double dec = p_num - (double)((int)p_num); int digit = 0; - if (p_decimals > MAX_DIGITS) - p_decimals = MAX_DIGITS; + if (p_decimals > MAX_DECIMALS) { + p_decimals = MAX_DECIMALS; + } int dec_int = 0; int dec_max = 0; @@ -1471,16 +1474,18 @@ String String::num(double p_num, int p_decimals) { digit++; if (p_decimals == -1) { - if (digit == MAX_DIGITS) //no point in going to infinite + if (digit == MAX_DECIMALS) { //no point in going to infinite break; + } if (dec - (double)((int)dec) < 1e-6) { break; } } - if (digit == p_decimals) + if (digit == p_decimals) { break; + } } dec *= 10; int last = (int)dec % 10; @@ -1616,7 +1621,15 @@ String String::num_real(double p_num) { double dec = p_num - (double)((int)p_num); int digit = 0; - int decimals = MAX_DIGITS; + +#if REAL_T_IS_DOUBLE + int decimals = 14 - (int)floor(log10(p_num)); +#else + int decimals = 6 - (int)floor(log10(p_num)); +#endif + if (decimals > MAX_DECIMALS) { + decimals = MAX_DECIMALS; + } int dec_int = 0; int dec_max = 0; diff --git a/doc/classes/JSON.xml b/doc/classes/JSON.xml index a9fb50c262..7baff7aa39 100644 --- a/doc/classes/JSON.xml +++ b/doc/classes/JSON.xml @@ -27,9 +27,12 @@ + + Converts a [Variant] var to JSON text and returns the result. Useful for serializing data to store or send over the network. [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a Variant to JSON text will convert all numerical values to [float] types. + [b]Note:[/b] If [code]full_precision[/code] is true, when printing floats, the unreliable digits are printed in addition to the reliable digits to guarantee exact decoding. Use [code]indent[/code] parameter to pretty print the output. [b]Example output:[/b] [codeblock] diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 9e974b6fdc..58620f2b3e 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -240,10 +240,10 @@ - + Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code]. - + The circle constant, the circumference of the unit circle in radians.