Browse Source

Merge pull request #108836 from aaronfranke/json-grisu

Use num_scientific (Grisu2) when stringifying JSON with full precision
Thaddeus Crews 3 weeks ago
parent
commit
e256771e86
3 changed files with 30 additions and 23 deletions
  1. 12 5
      core/io/json.cpp
  2. 4 4
      modules/gltf/gltf_document.cpp
  3. 14 14
      tests/core/io/test_json.h

+ 12 - 5
core/io/json.cpp

@@ -82,11 +82,18 @@ void JSON::_stringify(String &r_result, const Variant &p_var, const String &p_in
 				return;
 				return;
 			}
 			}
 
 
-			const double magnitude = std::log10(Math::abs(num));
-			const int total_digits = p_full_precision ? 17 : 14;
-			const int precision = MAX(1, total_digits - (int)Math::floor(magnitude));
-
-			r_result += String::num(num, precision);
+			if (p_full_precision) {
+				const String num_sci = String::num_scientific(num);
+				if (num_sci.contains_char('.') || num_sci.contains_char('e')) {
+					r_result += num_sci;
+				} else {
+					r_result += num_sci + ".0";
+				}
+			} else {
+				const double magnitude = std::log10(Math::abs(num));
+				const int precision = MAX(1, 14 - (int)Math::floor(magnitude));
+				r_result += String::num(num, precision);
+			}
 			return;
 			return;
 		}
 		}
 		case Variant::PACKED_INT32_ARRAY:
 		case Variant::PACKED_INT32_ARRAY:

+ 4 - 4
modules/gltf/gltf_document.cpp

@@ -8423,7 +8423,7 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> p_state, const String p_path)
 		constexpr uint32_t text_chunk_type = 0x4E4F534A; // The byte sequence "JSON" as little-endian.
 		constexpr uint32_t text_chunk_type = 0x4E4F534A; // The byte sequence "JSON" as little-endian.
 		constexpr uint32_t binary_chunk_type = 0x004E4942; // The byte sequence "BIN\0" as little-endian.
 		constexpr uint32_t binary_chunk_type = 0x004E4942; // The byte sequence "BIN\0" as little-endian.
 
 
-		String json_string = Variant(p_state->json).to_json_string();
+		String json_string = JSON::stringify(p_state->json, "", true, true);
 		CharString cs = json_string.utf8();
 		CharString cs = json_string.utf8();
 		uint64_t text_data_length = cs.length();
 		uint64_t text_data_length = cs.length();
 		uint64_t text_chunk_length = ((text_data_length + 3) & (~3));
 		uint64_t text_chunk_length = ((text_data_length + 3) & (~3));
@@ -8440,7 +8440,7 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> p_state, const String p_path)
 				err = _encode_buffer_bins(p_state, p_path);
 				err = _encode_buffer_bins(p_state, p_path);
 				ERR_FAIL_COND_V(err != OK, err);
 				ERR_FAIL_COND_V(err != OK, err);
 				// Since the buffer bins were re-encoded, we need to re-convert the JSON to string.
 				// Since the buffer bins were re-encoded, we need to re-convert the JSON to string.
-				json_string = Variant(p_state->json).to_json_string();
+				json_string = JSON::stringify(p_state->json, "", true, true);
 				cs = json_string.utf8();
 				cs = json_string.utf8();
 				text_data_length = cs.length();
 				text_data_length = cs.length();
 				text_chunk_length = ((text_data_length + 3) & (~3));
 				text_chunk_length = ((text_data_length + 3) & (~3));
@@ -8483,7 +8483,7 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> p_state, const String p_path)
 		ERR_FAIL_COND_V(file.is_null(), FAILED);
 		ERR_FAIL_COND_V(file.is_null(), FAILED);
 
 
 		file->create(FileAccess::ACCESS_RESOURCES);
 		file->create(FileAccess::ACCESS_RESOURCES);
-		String json = Variant(p_state->json).to_json_string();
+		String json = JSON::stringify(p_state->json, "", true, true);
 		file->store_string(json);
 		file->store_string(json);
 	}
 	}
 	return err;
 	return err;
@@ -8617,7 +8617,7 @@ PackedByteArray GLTFDocument::_serialize_glb_buffer(Ref<GLTFState> p_state, Erro
 		*r_err = err;
 		*r_err = err;
 	}
 	}
 	ERR_FAIL_COND_V(err != OK, PackedByteArray());
 	ERR_FAIL_COND_V(err != OK, PackedByteArray());
-	String json_string = Variant(p_state->json).to_json_string();
+	String json_string = JSON::stringify(p_state->json, "", true, true);
 
 
 	constexpr uint64_t header_size = 12;
 	constexpr uint64_t header_size = 12;
 	constexpr uint64_t chunk_header_size = 8;
 	constexpr uint64_t chunk_header_size = 8;

+ 14 - 14
tests/core/io/test_json.h

@@ -72,8 +72,8 @@ TEST_CASE("[JSON] Stringify arrays") {
 	CHECK(JSON::stringify(indented_array, "\t") == "[\n\t0,\n\t1,\n\t2,\n\t3,\n\t4,\n\t[\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t]\n]");
 	CHECK(JSON::stringify(indented_array, "\t") == "[\n\t0,\n\t1,\n\t2,\n\t3,\n\t4,\n\t[\n\t\t0,\n\t\t1,\n\t\t2,\n\t\t3,\n\t\t4\n\t]\n]");
 
 
 	Array full_precision_array;
 	Array full_precision_array;
-	full_precision_array.push_back(0.123456789012345677);
-	CHECK(JSON::stringify(full_precision_array, "", true, true) == "[0.123456789012345677]");
+	full_precision_array.push_back(0.12345678901234568);
+	CHECK(JSON::stringify(full_precision_array, "", true, true) == "[0.12345678901234568]");
 
 
 	ERR_PRINT_OFF
 	ERR_PRINT_OFF
 	Array self_array;
 	Array self_array;
@@ -110,8 +110,8 @@ TEST_CASE("[JSON] Stringify dictionaries") {
 	CHECK(JSON::stringify(outer) == "{\"inner\":{\"key\":\"value\"}}");
 	CHECK(JSON::stringify(outer) == "{\"inner\":{\"key\":\"value\"}}");
 
 
 	Dictionary full_precision_dictionary;
 	Dictionary full_precision_dictionary;
-	full_precision_dictionary["key"] = 0.123456789012345677;
-	CHECK(JSON::stringify(full_precision_dictionary, "", true, true) == "{\"key\":0.123456789012345677}");
+	full_precision_dictionary["key"] = 0.12345678901234568;
+	CHECK(JSON::stringify(full_precision_dictionary, "", true, true) == "{\"key\":0.12345678901234568}");
 
 
 	ERR_PRINT_OFF
 	ERR_PRINT_OFF
 	Dictionary self_dictionary;
 	Dictionary self_dictionary;
@@ -352,16 +352,16 @@ TEST_CASE("[JSON] Serialization") {
 
 
 	static FpTestCase fp_tests_full_precision[] = {
 	static FpTestCase fp_tests_full_precision[] = {
 		{ 0.0, "0.0" },
 		{ 0.0, "0.0" },
-		{ 1000.1234567890123456789, "1000.12345678901238" },
-		{ -1000.1234567890123456789, "-1000.12345678901238" },
-		{ DBL_MAX, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
-		{ DBL_MAX - 1, "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0" },
-		{ std::pow(2, 53), "9007199254740992.0" },
-		{ -std::pow(2, 53), "-9007199254740992.0" },
-		{ 0.00000000000000011, "0.00000000000000011" },
-		{ -0.00000000000000011, "-0.00000000000000011" },
-		{ 1.0 / 3.0, "0.333333333333333315" },
-		{ 0.9999999999999999, "0.999999999999999889" },
+		{ 1000.1234567890123456789, "1000.1234567890124" },
+		{ -1000.1234567890123456789, "-1000.1234567890124" },
+		{ DBL_MAX, "1.7976931348623157e+308" },
+		{ DBL_MAX - 1, "1.7976931348623157e+308" },
+		{ std::pow(2, 53), "9.007199254740992e+15" },
+		{ -std::pow(2, 53), "-9.007199254740992e+15" },
+		{ 0.00000000000000011, "1.1e-16" },
+		{ -0.00000000000000011, "-1.1e-16" },
+		{ 1.0 / 3.0, "0.3333333333333333" },
+		{ 0.9999999999999999, "0.9999999999999999" },
 		{ 1.0000000000000001, "1.0" },
 		{ 1.0000000000000001, "1.0" },
 	};
 	};