瀏覽代碼

Merge pull request #98750 from aaronfranke/grisu

Use Grisu2 algorithm in `String::num_scientific` to fix serializing
Thaddeus Crews 3 月之前
父節點
當前提交
6258a3e224

+ 6 - 0
COPYRIGHT.txt

@@ -293,6 +293,12 @@ Comment: Graphite engine
 Copyright: 2010, SIL International
 Copyright: 2010, SIL International
 License: Expat
 License: Expat
 
 
+Files: thirdparty/grisu2/grisu2.h
+Comment: Grisu2 float serialization algorithm
+Copyright: 2009, Florian Loitsch
+ 2018-2023, The simdjson authors
+License: Expat and Apache
+
 Files: thirdparty/harfbuzz/*
 Files: thirdparty/harfbuzz/*
 Comment: HarfBuzz text shaping library
 Comment: HarfBuzz text shaping library
 Copyright: 2010-2022, Google, Inc.
 Copyright: 2010-2022, Google, Inc.

+ 16 - 2
core/doc_data.cpp

@@ -31,10 +31,24 @@
 #include "doc_data.h"
 #include "doc_data.h"
 
 
 String DocData::get_default_value_string(const Variant &p_value) {
 String DocData::get_default_value_string(const Variant &p_value) {
-	if (p_value.get_type() == Variant::ARRAY) {
+	const Variant::Type type = p_value.get_type();
+	if (type == Variant::ARRAY) {
 		return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace_char('\n', ' ');
 		return Variant(Array(p_value, 0, StringName(), Variant())).get_construct_string().replace_char('\n', ' ');
-	} else if (p_value.get_type() == Variant::DICTIONARY) {
+	} else if (type == Variant::DICTIONARY) {
 		return Variant(Dictionary(p_value, 0, StringName(), Variant(), 0, StringName(), Variant())).get_construct_string().replace_char('\n', ' ');
 		return Variant(Dictionary(p_value, 0, StringName(), Variant(), 0, StringName(), Variant())).get_construct_string().replace_char('\n', ' ');
+	} else if (type == Variant::INT) {
+		return itos(p_value);
+	} else if (type == Variant::FLOAT) {
+		// Since some values are 32-bit internally, use 32-bit for all
+		// documentation values to avoid garbage digits at the end.
+		const String s = String::num_scientific((float)p_value);
+		// Use float literals for floats in the documentation for clarity.
+		if (s != "inf" && s != "-inf" && s != "nan") {
+			if (!s.contains_char('.') && !s.contains_char('e')) {
+				return s + ".0";
+			}
+		}
+		return s;
 	} else {
 	} else {
 		return p_value.get_construct_string().replace_char('\n', ' ');
 		return p_value.get_construct_string().replace_char('\n', ' ');
 	}
 	}

+ 13 - 21
core/string/ustring.cpp

@@ -43,6 +43,8 @@
 #include "core/variant/variant.h"
 #include "core/variant/variant.h"
 #include "core/version_generated.gen.h"
 #include "core/version_generated.gen.h"
 
 
+#include "thirdparty/grisu2/grisu2.h"
+
 #ifdef _MSC_VER
 #ifdef _MSC_VER
 #define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
 #define _CRT_SECURE_NO_WARNINGS // to disable build-time warning which suggested to use strcpy_s instead strcpy
 #endif
 #endif
@@ -1656,28 +1658,18 @@ String String::num_scientific(double p_num) {
 	if (Math::is_nan(p_num) || Math::is_inf(p_num)) {
 	if (Math::is_nan(p_num) || Math::is_inf(p_num)) {
 		return num(p_num, 0);
 		return num(p_num, 0);
 	}
 	}
+	char buffer[256];
+	char *last = grisu2::to_chars(buffer, p_num);
+	return String::ascii(Span(buffer, last - buffer));
+}
 
 
-	char buf[256];
-
-#if defined(__GNUC__) || defined(_MSC_VER)
-
-#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
-	// MinGW requires _set_output_format() to conform to C99 output for printf
-	unsigned int old_exponent_format = _set_output_format(_TWO_DIGIT_EXPONENT);
-#endif
-	snprintf(buf, 256, "%lg", p_num);
-
-#if defined(__MINGW32__) && defined(_TWO_DIGIT_EXPONENT) && !defined(_UCRT)
-	_set_output_format(old_exponent_format);
-#endif
-
-#else
-	sprintf(buf, "%.16lg", p_num);
-#endif
-
-	buf[255] = 0;
-
-	return buf;
+String String::num_scientific(float p_num) {
+	if (Math::is_nan(p_num) || Math::is_inf(p_num)) {
+		return num(p_num, 0);
+	}
+	char buffer[256];
+	char *last = grisu2::to_chars(buffer, p_num);
+	return String::ascii(Span(buffer, last - buffer));
 }
 }
 
 
 String String::md5(const uint8_t *p_md5) {
 String String::md5(const uint8_t *p_md5) {

+ 1 - 0
core/string/ustring.h

@@ -446,6 +446,7 @@ public:
 	String unquote() const;
 	String unquote() const;
 	static String num(double p_num, int p_decimals = -1);
 	static String num(double p_num, int p_decimals = -1);
 	static String num_scientific(double p_num);
 	static String num_scientific(double p_num);
+	static String num_scientific(float p_num);
 	static String num_real(double p_num, bool p_trailing = true);
 	static String num_real(double p_num, bool p_trailing = true);
 	static String num_real(float p_num, bool p_trailing = true);
 	static String num_real(float p_num, bool p_trailing = true);
 	static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
 	static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);

+ 11 - 1
core/variant/variant_call.cpp

@@ -1697,6 +1697,16 @@ StringName Variant::get_enum_for_enumeration(Variant::Type p_type, const StringN
 	register_builtin_method<Method_##m_type##_##m_method>(sarray(), m_default_args);
 	register_builtin_method<Method_##m_type##_##m_method>(sarray(), m_default_args);
 #endif // DEBUG_ENABLED
 #endif // DEBUG_ENABLED
 
 
+#ifdef DEBUG_ENABLED
+#define bind_static_methodv(m_type, m_name, m_method, m_arg_names, m_default_args) \
+	STATIC_METHOD_CLASS(m_type, m_name, m_method);                                 \
+	register_builtin_method<Method_##m_type##_##m_name>(m_arg_names, m_default_args);
+#else
+#define bind_static_methodv(m_type, m_name, m_method, m_arg_names, m_default_args) \
+	STATIC_METHOD_CLASS(m_type, m_name, m_method);                                 \
+	register_builtin_method<Method_##m_type##_##m_name>(sarray(), m_default_args);
+#endif
+
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 #define bind_methodv(m_type, m_name, m_method, m_arg_names, m_default_args) \
 #define bind_methodv(m_type, m_name, m_method, m_arg_names, m_default_args) \
 	METHOD_CLASS(m_type, m_name, m_method);                                 \
 	METHOD_CLASS(m_type, m_name, m_method);                                 \
@@ -1882,7 +1892,7 @@ static void _register_variant_builtin_methods_string() {
 	bind_string_method(to_multibyte_char_buffer, sarray("encoding"), varray(String()));
 	bind_string_method(to_multibyte_char_buffer, sarray("encoding"), varray(String()));
 	bind_string_method(hex_decode, sarray(), varray());
 	bind_string_method(hex_decode, sarray(), varray());
 
 
-	bind_static_method(String, num_scientific, sarray("number"), varray());
+	bind_static_methodv(String, num_scientific, static_cast<String (*)(double)>(&String::num_scientific), sarray("number"), varray());
 	bind_static_method(String, num, sarray("number", "decimals"), varray(-1));
 	bind_static_method(String, num, sarray("number", "decimals"), varray(-1));
 	bind_static_method(String, num_int64, sarray("number", "base", "capitalize_hex"), varray(10, false));
 	bind_static_method(String, num_int64, sarray("number", "base", "capitalize_hex"), varray(10, false));
 	bind_static_method(String, num_uint64, sarray("number", "base", "capitalize_hex"), varray(10, false));
 	bind_static_method(String, num_uint64, sarray("number", "base", "capitalize_hex"), varray(10, false));

+ 30 - 16
core/variant/variant_parser.cpp

@@ -1934,22 +1934,30 @@ Error VariantParser::parse(Stream *p_stream, Variant &r_ret, String &r_err_str,
 //////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////
 
 
+// These two functions serialize floats or doubles using num_scientific to ensure
+// it can be read back in the same way (except collapsing -0 to 0, and NaN values).
+static String rtos_fix(float p_value, bool p_compat) {
+	if (p_value == 0.0f) {
+		return "0"; // Avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist.
+	} else if (p_compat) {
+		// Write old inf_neg for compatibility.
+		if (std::isinf(p_value) && p_value < 0.0f) {
+			return "inf_neg";
+		}
+	}
+	return String::num_scientific(p_value);
+}
+
 static String rtos_fix(double p_value, bool p_compat) {
 static String rtos_fix(double p_value, bool p_compat) {
 	if (p_value == 0.0) {
 	if (p_value == 0.0) {
-		return "0"; //avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist.
-	} else if (std::isnan(p_value)) {
-		return "nan";
-	} else if (std::isinf(p_value)) {
-		if (p_value > 0) {
-			return "inf";
-		} else if (p_compat) {
+		return "0"; // Avoid negative zero (-0) being written, which may annoy git, svn, etc. for changes when they don't exist.
+	} else if (p_compat) {
+		// Write old inf_neg for compatibility.
+		if (std::isinf(p_value) && p_value < 0.0) {
 			return "inf_neg";
 			return "inf_neg";
-		} else {
-			return "-inf";
 		}
 		}
-	} else {
-		return rtoss(p_value);
 	}
 	}
+	return String::num_scientific(p_value);
 }
 }
 
 
 Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count, bool p_compat) {
 Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count, bool p_compat) {
@@ -1964,11 +1972,17 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 			p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t()));
 			p_store_string_func(p_store_string_ud, itos(p_variant.operator int64_t()));
 		} break;
 		} break;
 		case Variant::FLOAT: {
 		case Variant::FLOAT: {
-			String s = rtos_fix(p_variant.operator double(), p_compat);
-			if (s != "inf" && s != "-inf" && s != "nan") {
-				if (!s.contains_char('.') && !s.contains_char('e') && !s.contains_char('E')) {
-					s += ".0";
-				}
+			const double value = p_variant.operator double();
+			String s;
+			// Hack to avoid garbage digits when the underlying float is 32-bit.
+			if ((double)(float)value == value) {
+				s = rtos_fix((float)value, p_compat);
+			} else {
+				s = rtos_fix(value, p_compat);
+			}
+			// Append ".0" to floats to ensure they are float literals.
+			if (s != "inf" && s != "-inf" && s != "nan" && !s.contains_char('.') && !s.contains_char('e') && !s.contains_char('E')) {
+				s += ".0";
 			}
 			}
 			p_store_string_func(p_store_string_ud, s);
 			p_store_string_func(p_store_string_ud, s);
 		} break;
 		} break;

+ 1 - 1
doc/classes/Animation.xml

@@ -689,7 +689,7 @@
 		<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode" default="0">
 		<member name="loop_mode" type="int" setter="set_loop_mode" getter="get_loop_mode" enum="Animation.LoopMode" default="0">
 			Determines the behavior of both ends of the animation timeline during animation playback. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation.
 			Determines the behavior of both ends of the animation timeline during animation playback. This is used for correct interpolation of animation cycles, and for hinting the player that it must restart the animation.
 		</member>
 		</member>
-		<member name="step" type="float" setter="set_step" getter="get_step" default="0.0333333">
+		<member name="step" type="float" setter="set_step" getter="get_step" default="0.033333335">
 			The animation step value.
 			The animation step value.
 		</member>
 		</member>
 	</members>
 	</members>

+ 2 - 2
doc/classes/CharacterBody2D.xml

@@ -154,7 +154,7 @@
 			If [code]false[/code] (by default), the body will move faster on downward slopes and slower on upward slopes.
 			If [code]false[/code] (by default), the body will move faster on downward slopes and slower on upward slopes.
 			If [code]true[/code], the body will always move at the same speed on the ground no matter the slope. Note that you need to use [member floor_snap_length] to stick along a downward slope at constant speed.
 			If [code]true[/code], the body will always move at the same speed on the ground no matter the slope. Note that you need to use [member floor_snap_length] to stick along a downward slope at constant speed.
 		</member>
 		</member>
-		<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398">
+		<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.7853982">
 			Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
 			Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
 		</member>
 		</member>
 		<member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="1.0">
 		<member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="1.0">
@@ -195,7 +195,7 @@
 		<member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)">
 		<member name="velocity" type="Vector2" setter="set_velocity" getter="get_velocity" default="Vector2(0, 0)">
 			Current velocity vector in pixels per second, used and modified during calls to [method move_and_slide].
 			Current velocity vector in pixels per second, used and modified during calls to [method move_and_slide].
 		</member>
 		</member>
-		<member name="wall_min_slide_angle" type="float" setter="set_wall_min_slide_angle" getter="get_wall_min_slide_angle" default="0.261799">
+		<member name="wall_min_slide_angle" type="float" setter="set_wall_min_slide_angle" getter="get_wall_min_slide_angle" default="0.2617994">
 			Minimum angle (in radians) where the body is allowed to slide when it encounters a wall. The default value equals 15 degrees. This property only affects movement when [member motion_mode] is [constant MOTION_MODE_FLOATING].
 			Minimum angle (in radians) where the body is allowed to slide when it encounters a wall. The default value equals 15 degrees. This property only affects movement when [member motion_mode] is [constant MOTION_MODE_FLOATING].
 		</member>
 		</member>
 	</members>
 	</members>

+ 2 - 2
doc/classes/CharacterBody3D.xml

@@ -145,7 +145,7 @@
 			If [code]false[/code] (by default), the body will move faster on downward slopes and slower on upward slopes.
 			If [code]false[/code] (by default), the body will move faster on downward slopes and slower on upward slopes.
 			If [code]true[/code], the body will always move at the same speed on the ground no matter the slope. Note that you need to use [member floor_snap_length] to stick along a downward slope at constant speed.
 			If [code]true[/code], the body will always move at the same speed on the ground no matter the slope. Note that you need to use [member floor_snap_length] to stick along a downward slope at constant speed.
 		</member>
 		</member>
-		<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.785398">
+		<member name="floor_max_angle" type="float" setter="set_floor_max_angle" getter="get_floor_max_angle" default="0.7853982">
 			Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
 			Maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall, when calling [method move_and_slide]. The default value equals 45 degrees.
 		</member>
 		</member>
 		<member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="0.1">
 		<member name="floor_snap_length" type="float" setter="set_floor_snap_length" getter="get_floor_snap_length" default="0.1">
@@ -186,7 +186,7 @@
 		<member name="velocity" type="Vector3" setter="set_velocity" getter="get_velocity" default="Vector3(0, 0, 0)">
 		<member name="velocity" type="Vector3" setter="set_velocity" getter="get_velocity" default="Vector3(0, 0, 0)">
 			Current velocity vector (typically meters per second), used and modified during calls to [method move_and_slide].
 			Current velocity vector (typically meters per second), used and modified during calls to [method move_and_slide].
 		</member>
 		</member>
-		<member name="wall_min_slide_angle" type="float" setter="set_wall_min_slide_angle" getter="get_wall_min_slide_angle" default="0.261799">
+		<member name="wall_min_slide_angle" type="float" setter="set_wall_min_slide_angle" getter="get_wall_min_slide_angle" default="0.2617994">
 			Minimum angle (in radians) where the body is allowed to slide when it encounters a wall. The default value equals 15 degrees. When [member motion_mode] is [constant MOTION_MODE_GROUNDED], it only affects movement if [member floor_block_on_wall] is [code]true[/code].
 			Minimum angle (in radians) where the body is allowed to slide when it encounters a wall. The default value equals 15 degrees. When [member motion_mode] is [constant MOTION_MODE_GROUNDED], it only affects movement if [member floor_block_on_wall] is [code]true[/code].
 		</member>
 		</member>
 	</members>
 	</members>

+ 131 - 131
doc/classes/Color.xml

@@ -538,343 +538,343 @@
 		</member>
 		</member>
 	</members>
 	</members>
 	<constants>
 	<constants>
-		<constant name="ALICE_BLUE" value="Color(0.941176, 0.972549, 1, 1)">
+		<constant name="ALICE_BLUE" value="Color(0.9411765, 0.972549, 1, 1)">
 			Alice blue color.
 			Alice blue color.
 		</constant>
 		</constant>
-		<constant name="ANTIQUE_WHITE" value="Color(0.980392, 0.921569, 0.843137, 1)">
+		<constant name="ANTIQUE_WHITE" value="Color(0.98039216, 0.92156863, 0.84313726, 1)">
 			Antique white color.
 			Antique white color.
 		</constant>
 		</constant>
 		<constant name="AQUA" value="Color(0, 1, 1, 1)">
 		<constant name="AQUA" value="Color(0, 1, 1, 1)">
 			Aqua color.
 			Aqua color.
 		</constant>
 		</constant>
-		<constant name="AQUAMARINE" value="Color(0.498039, 1, 0.831373, 1)">
+		<constant name="AQUAMARINE" value="Color(0.49803922, 1, 0.83137256, 1)">
 			Aquamarine color.
 			Aquamarine color.
 		</constant>
 		</constant>
-		<constant name="AZURE" value="Color(0.941176, 1, 1, 1)">
+		<constant name="AZURE" value="Color(0.9411765, 1, 1, 1)">
 			Azure color.
 			Azure color.
 		</constant>
 		</constant>
-		<constant name="BEIGE" value="Color(0.960784, 0.960784, 0.862745, 1)">
+		<constant name="BEIGE" value="Color(0.9607843, 0.9607843, 0.8627451, 1)">
 			Beige color.
 			Beige color.
 		</constant>
 		</constant>
-		<constant name="BISQUE" value="Color(1, 0.894118, 0.768627, 1)">
+		<constant name="BISQUE" value="Color(1, 0.89411765, 0.76862746, 1)">
 			Bisque color.
 			Bisque color.
 		</constant>
 		</constant>
 		<constant name="BLACK" value="Color(0, 0, 0, 1)">
 		<constant name="BLACK" value="Color(0, 0, 0, 1)">
 			Black color. In GDScript, this is the default value of any color.
 			Black color. In GDScript, this is the default value of any color.
 		</constant>
 		</constant>
-		<constant name="BLANCHED_ALMOND" value="Color(1, 0.921569, 0.803922, 1)">
+		<constant name="BLANCHED_ALMOND" value="Color(1, 0.92156863, 0.8039216, 1)">
 			Blanched almond color.
 			Blanched almond color.
 		</constant>
 		</constant>
 		<constant name="BLUE" value="Color(0, 0, 1, 1)">
 		<constant name="BLUE" value="Color(0, 0, 1, 1)">
 			Blue color.
 			Blue color.
 		</constant>
 		</constant>
-		<constant name="BLUE_VIOLET" value="Color(0.541176, 0.168627, 0.886275, 1)">
+		<constant name="BLUE_VIOLET" value="Color(0.5411765, 0.16862746, 0.8862745, 1)">
 			Blue violet color.
 			Blue violet color.
 		</constant>
 		</constant>
-		<constant name="BROWN" value="Color(0.647059, 0.164706, 0.164706, 1)">
+		<constant name="BROWN" value="Color(0.64705884, 0.16470589, 0.16470589, 1)">
 			Brown color.
 			Brown color.
 		</constant>
 		</constant>
-		<constant name="BURLYWOOD" value="Color(0.870588, 0.721569, 0.529412, 1)">
+		<constant name="BURLYWOOD" value="Color(0.87058824, 0.72156864, 0.5294118, 1)">
 			Burlywood color.
 			Burlywood color.
 		</constant>
 		</constant>
-		<constant name="CADET_BLUE" value="Color(0.372549, 0.619608, 0.627451, 1)">
+		<constant name="CADET_BLUE" value="Color(0.37254903, 0.61960787, 0.627451, 1)">
 			Cadet blue color.
 			Cadet blue color.
 		</constant>
 		</constant>
-		<constant name="CHARTREUSE" value="Color(0.498039, 1, 0, 1)">
+		<constant name="CHARTREUSE" value="Color(0.49803922, 1, 0, 1)">
 			Chartreuse color.
 			Chartreuse color.
 		</constant>
 		</constant>
-		<constant name="CHOCOLATE" value="Color(0.823529, 0.411765, 0.117647, 1)">
+		<constant name="CHOCOLATE" value="Color(0.8235294, 0.4117647, 0.11764706, 1)">
 			Chocolate color.
 			Chocolate color.
 		</constant>
 		</constant>
-		<constant name="CORAL" value="Color(1, 0.498039, 0.313726, 1)">
+		<constant name="CORAL" value="Color(1, 0.49803922, 0.3137255, 1)">
 			Coral color.
 			Coral color.
 		</constant>
 		</constant>
-		<constant name="CORNFLOWER_BLUE" value="Color(0.392157, 0.584314, 0.929412, 1)">
+		<constant name="CORNFLOWER_BLUE" value="Color(0.39215687, 0.58431375, 0.92941177, 1)">
 			Cornflower blue color.
 			Cornflower blue color.
 		</constant>
 		</constant>
-		<constant name="CORNSILK" value="Color(1, 0.972549, 0.862745, 1)">
+		<constant name="CORNSILK" value="Color(1, 0.972549, 0.8627451, 1)">
 			Cornsilk color.
 			Cornsilk color.
 		</constant>
 		</constant>
-		<constant name="CRIMSON" value="Color(0.862745, 0.0784314, 0.235294, 1)">
+		<constant name="CRIMSON" value="Color(0.8627451, 0.078431375, 0.23529412, 1)">
 			Crimson color.
 			Crimson color.
 		</constant>
 		</constant>
 		<constant name="CYAN" value="Color(0, 1, 1, 1)">
 		<constant name="CYAN" value="Color(0, 1, 1, 1)">
 			Cyan color.
 			Cyan color.
 		</constant>
 		</constant>
-		<constant name="DARK_BLUE" value="Color(0, 0, 0.545098, 1)">
+		<constant name="DARK_BLUE" value="Color(0, 0, 0.54509807, 1)">
 			Dark blue color.
 			Dark blue color.
 		</constant>
 		</constant>
-		<constant name="DARK_CYAN" value="Color(0, 0.545098, 0.545098, 1)">
+		<constant name="DARK_CYAN" value="Color(0, 0.54509807, 0.54509807, 1)">
 			Dark cyan color.
 			Dark cyan color.
 		</constant>
 		</constant>
-		<constant name="DARK_GOLDENROD" value="Color(0.721569, 0.52549, 0.0431373, 1)">
+		<constant name="DARK_GOLDENROD" value="Color(0.72156864, 0.5254902, 0.043137256, 1)">
 			Dark goldenrod color.
 			Dark goldenrod color.
 		</constant>
 		</constant>
-		<constant name="DARK_GRAY" value="Color(0.662745, 0.662745, 0.662745, 1)">
+		<constant name="DARK_GRAY" value="Color(0.6627451, 0.6627451, 0.6627451, 1)">
 			Dark gray color.
 			Dark gray color.
 		</constant>
 		</constant>
-		<constant name="DARK_GREEN" value="Color(0, 0.392157, 0, 1)">
+		<constant name="DARK_GREEN" value="Color(0, 0.39215687, 0, 1)">
 			Dark green color.
 			Dark green color.
 		</constant>
 		</constant>
-		<constant name="DARK_KHAKI" value="Color(0.741176, 0.717647, 0.419608, 1)">
+		<constant name="DARK_KHAKI" value="Color(0.7411765, 0.7176471, 0.41960785, 1)">
 			Dark khaki color.
 			Dark khaki color.
 		</constant>
 		</constant>
-		<constant name="DARK_MAGENTA" value="Color(0.545098, 0, 0.545098, 1)">
+		<constant name="DARK_MAGENTA" value="Color(0.54509807, 0, 0.54509807, 1)">
 			Dark magenta color.
 			Dark magenta color.
 		</constant>
 		</constant>
-		<constant name="DARK_OLIVE_GREEN" value="Color(0.333333, 0.419608, 0.184314, 1)">
+		<constant name="DARK_OLIVE_GREEN" value="Color(0.33333334, 0.41960785, 0.18431373, 1)">
 			Dark olive green color.
 			Dark olive green color.
 		</constant>
 		</constant>
-		<constant name="DARK_ORANGE" value="Color(1, 0.54902, 0, 1)">
+		<constant name="DARK_ORANGE" value="Color(1, 0.54901963, 0, 1)">
 			Dark orange color.
 			Dark orange color.
 		</constant>
 		</constant>
-		<constant name="DARK_ORCHID" value="Color(0.6, 0.196078, 0.8, 1)">
+		<constant name="DARK_ORCHID" value="Color(0.6, 0.19607843, 0.8, 1)">
 			Dark orchid color.
 			Dark orchid color.
 		</constant>
 		</constant>
-		<constant name="DARK_RED" value="Color(0.545098, 0, 0, 1)">
+		<constant name="DARK_RED" value="Color(0.54509807, 0, 0, 1)">
 			Dark red color.
 			Dark red color.
 		</constant>
 		</constant>
-		<constant name="DARK_SALMON" value="Color(0.913725, 0.588235, 0.478431, 1)">
+		<constant name="DARK_SALMON" value="Color(0.9137255, 0.5882353, 0.47843137, 1)">
 			Dark salmon color.
 			Dark salmon color.
 		</constant>
 		</constant>
-		<constant name="DARK_SEA_GREEN" value="Color(0.560784, 0.737255, 0.560784, 1)">
+		<constant name="DARK_SEA_GREEN" value="Color(0.56078434, 0.7372549, 0.56078434, 1)">
 			Dark sea green color.
 			Dark sea green color.
 		</constant>
 		</constant>
-		<constant name="DARK_SLATE_BLUE" value="Color(0.282353, 0.239216, 0.545098, 1)">
+		<constant name="DARK_SLATE_BLUE" value="Color(0.28235295, 0.23921569, 0.54509807, 1)">
 			Dark slate blue color.
 			Dark slate blue color.
 		</constant>
 		</constant>
-		<constant name="DARK_SLATE_GRAY" value="Color(0.184314, 0.309804, 0.309804, 1)">
+		<constant name="DARK_SLATE_GRAY" value="Color(0.18431373, 0.30980393, 0.30980393, 1)">
 			Dark slate gray color.
 			Dark slate gray color.
 		</constant>
 		</constant>
-		<constant name="DARK_TURQUOISE" value="Color(0, 0.807843, 0.819608, 1)">
+		<constant name="DARK_TURQUOISE" value="Color(0, 0.80784315, 0.81960785, 1)">
 			Dark turquoise color.
 			Dark turquoise color.
 		</constant>
 		</constant>
-		<constant name="DARK_VIOLET" value="Color(0.580392, 0, 0.827451, 1)">
+		<constant name="DARK_VIOLET" value="Color(0.5803922, 0, 0.827451, 1)">
 			Dark violet color.
 			Dark violet color.
 		</constant>
 		</constant>
-		<constant name="DEEP_PINK" value="Color(1, 0.0784314, 0.576471, 1)">
+		<constant name="DEEP_PINK" value="Color(1, 0.078431375, 0.5764706, 1)">
 			Deep pink color.
 			Deep pink color.
 		</constant>
 		</constant>
-		<constant name="DEEP_SKY_BLUE" value="Color(0, 0.74902, 1, 1)">
+		<constant name="DEEP_SKY_BLUE" value="Color(0, 0.7490196, 1, 1)">
 			Deep sky blue color.
 			Deep sky blue color.
 		</constant>
 		</constant>
-		<constant name="DIM_GRAY" value="Color(0.411765, 0.411765, 0.411765, 1)">
+		<constant name="DIM_GRAY" value="Color(0.4117647, 0.4117647, 0.4117647, 1)">
 			Dim gray color.
 			Dim gray color.
 		</constant>
 		</constant>
-		<constant name="DODGER_BLUE" value="Color(0.117647, 0.564706, 1, 1)">
+		<constant name="DODGER_BLUE" value="Color(0.11764706, 0.5647059, 1, 1)">
 			Dodger blue color.
 			Dodger blue color.
 		</constant>
 		</constant>
-		<constant name="FIREBRICK" value="Color(0.698039, 0.133333, 0.133333, 1)">
+		<constant name="FIREBRICK" value="Color(0.69803923, 0.13333334, 0.13333334, 1)">
 			Firebrick color.
 			Firebrick color.
 		</constant>
 		</constant>
-		<constant name="FLORAL_WHITE" value="Color(1, 0.980392, 0.941176, 1)">
+		<constant name="FLORAL_WHITE" value="Color(1, 0.98039216, 0.9411765, 1)">
 			Floral white color.
 			Floral white color.
 		</constant>
 		</constant>
-		<constant name="FOREST_GREEN" value="Color(0.133333, 0.545098, 0.133333, 1)">
+		<constant name="FOREST_GREEN" value="Color(0.13333334, 0.54509807, 0.13333334, 1)">
 			Forest green color.
 			Forest green color.
 		</constant>
 		</constant>
 		<constant name="FUCHSIA" value="Color(1, 0, 1, 1)">
 		<constant name="FUCHSIA" value="Color(1, 0, 1, 1)">
 			Fuchsia color.
 			Fuchsia color.
 		</constant>
 		</constant>
-		<constant name="GAINSBORO" value="Color(0.862745, 0.862745, 0.862745, 1)">
+		<constant name="GAINSBORO" value="Color(0.8627451, 0.8627451, 0.8627451, 1)">
 			Gainsboro color.
 			Gainsboro color.
 		</constant>
 		</constant>
 		<constant name="GHOST_WHITE" value="Color(0.972549, 0.972549, 1, 1)">
 		<constant name="GHOST_WHITE" value="Color(0.972549, 0.972549, 1, 1)">
 			Ghost white color.
 			Ghost white color.
 		</constant>
 		</constant>
-		<constant name="GOLD" value="Color(1, 0.843137, 0, 1)">
+		<constant name="GOLD" value="Color(1, 0.84313726, 0, 1)">
 			Gold color.
 			Gold color.
 		</constant>
 		</constant>
-		<constant name="GOLDENROD" value="Color(0.854902, 0.647059, 0.12549, 1)">
+		<constant name="GOLDENROD" value="Color(0.85490197, 0.64705884, 0.1254902, 1)">
 			Goldenrod color.
 			Goldenrod color.
 		</constant>
 		</constant>
-		<constant name="GRAY" value="Color(0.745098, 0.745098, 0.745098, 1)">
+		<constant name="GRAY" value="Color(0.74509805, 0.74509805, 0.74509805, 1)">
 			Gray color.
 			Gray color.
 		</constant>
 		</constant>
 		<constant name="GREEN" value="Color(0, 1, 0, 1)">
 		<constant name="GREEN" value="Color(0, 1, 0, 1)">
 			Green color.
 			Green color.
 		</constant>
 		</constant>
-		<constant name="GREEN_YELLOW" value="Color(0.678431, 1, 0.184314, 1)">
+		<constant name="GREEN_YELLOW" value="Color(0.6784314, 1, 0.18431373, 1)">
 			Green yellow color.
 			Green yellow color.
 		</constant>
 		</constant>
-		<constant name="HONEYDEW" value="Color(0.941176, 1, 0.941176, 1)">
+		<constant name="HONEYDEW" value="Color(0.9411765, 1, 0.9411765, 1)">
 			Honeydew color.
 			Honeydew color.
 		</constant>
 		</constant>
-		<constant name="HOT_PINK" value="Color(1, 0.411765, 0.705882, 1)">
+		<constant name="HOT_PINK" value="Color(1, 0.4117647, 0.7058824, 1)">
 			Hot pink color.
 			Hot pink color.
 		</constant>
 		</constant>
-		<constant name="INDIAN_RED" value="Color(0.803922, 0.360784, 0.360784, 1)">
+		<constant name="INDIAN_RED" value="Color(0.8039216, 0.36078432, 0.36078432, 1)">
 			Indian red color.
 			Indian red color.
 		</constant>
 		</constant>
-		<constant name="INDIGO" value="Color(0.294118, 0, 0.509804, 1)">
+		<constant name="INDIGO" value="Color(0.29411766, 0, 0.50980395, 1)">
 			Indigo color.
 			Indigo color.
 		</constant>
 		</constant>
-		<constant name="IVORY" value="Color(1, 1, 0.941176, 1)">
+		<constant name="IVORY" value="Color(1, 1, 0.9411765, 1)">
 			Ivory color.
 			Ivory color.
 		</constant>
 		</constant>
-		<constant name="KHAKI" value="Color(0.941176, 0.901961, 0.54902, 1)">
+		<constant name="KHAKI" value="Color(0.9411765, 0.9019608, 0.54901963, 1)">
 			Khaki color.
 			Khaki color.
 		</constant>
 		</constant>
-		<constant name="LAVENDER" value="Color(0.901961, 0.901961, 0.980392, 1)">
+		<constant name="LAVENDER" value="Color(0.9019608, 0.9019608, 0.98039216, 1)">
 			Lavender color.
 			Lavender color.
 		</constant>
 		</constant>
-		<constant name="LAVENDER_BLUSH" value="Color(1, 0.941176, 0.960784, 1)">
+		<constant name="LAVENDER_BLUSH" value="Color(1, 0.9411765, 0.9607843, 1)">
 			Lavender blush color.
 			Lavender blush color.
 		</constant>
 		</constant>
-		<constant name="LAWN_GREEN" value="Color(0.486275, 0.988235, 0, 1)">
+		<constant name="LAWN_GREEN" value="Color(0.4862745, 0.9882353, 0, 1)">
 			Lawn green color.
 			Lawn green color.
 		</constant>
 		</constant>
-		<constant name="LEMON_CHIFFON" value="Color(1, 0.980392, 0.803922, 1)">
+		<constant name="LEMON_CHIFFON" value="Color(1, 0.98039216, 0.8039216, 1)">
 			Lemon chiffon color.
 			Lemon chiffon color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_BLUE" value="Color(0.678431, 0.847059, 0.901961, 1)">
+		<constant name="LIGHT_BLUE" value="Color(0.6784314, 0.84705883, 0.9019608, 1)">
 			Light blue color.
 			Light blue color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_CORAL" value="Color(0.941176, 0.501961, 0.501961, 1)">
+		<constant name="LIGHT_CORAL" value="Color(0.9411765, 0.5019608, 0.5019608, 1)">
 			Light coral color.
 			Light coral color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_CYAN" value="Color(0.878431, 1, 1, 1)">
+		<constant name="LIGHT_CYAN" value="Color(0.8784314, 1, 1, 1)">
 			Light cyan color.
 			Light cyan color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_GOLDENROD" value="Color(0.980392, 0.980392, 0.823529, 1)">
+		<constant name="LIGHT_GOLDENROD" value="Color(0.98039216, 0.98039216, 0.8235294, 1)">
 			Light goldenrod color.
 			Light goldenrod color.
 		</constant>
 		</constant>
 		<constant name="LIGHT_GRAY" value="Color(0.827451, 0.827451, 0.827451, 1)">
 		<constant name="LIGHT_GRAY" value="Color(0.827451, 0.827451, 0.827451, 1)">
 			Light gray color.
 			Light gray color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_GREEN" value="Color(0.564706, 0.933333, 0.564706, 1)">
+		<constant name="LIGHT_GREEN" value="Color(0.5647059, 0.93333334, 0.5647059, 1)">
 			Light green color.
 			Light green color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_PINK" value="Color(1, 0.713726, 0.756863, 1)">
+		<constant name="LIGHT_PINK" value="Color(1, 0.7137255, 0.75686276, 1)">
 			Light pink color.
 			Light pink color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_SALMON" value="Color(1, 0.627451, 0.478431, 1)">
+		<constant name="LIGHT_SALMON" value="Color(1, 0.627451, 0.47843137, 1)">
 			Light salmon color.
 			Light salmon color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_SEA_GREEN" value="Color(0.12549, 0.698039, 0.666667, 1)">
+		<constant name="LIGHT_SEA_GREEN" value="Color(0.1254902, 0.69803923, 0.6666667, 1)">
 			Light sea green color.
 			Light sea green color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_SKY_BLUE" value="Color(0.529412, 0.807843, 0.980392, 1)">
+		<constant name="LIGHT_SKY_BLUE" value="Color(0.5294118, 0.80784315, 0.98039216, 1)">
 			Light sky blue color.
 			Light sky blue color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_SLATE_GRAY" value="Color(0.466667, 0.533333, 0.6, 1)">
+		<constant name="LIGHT_SLATE_GRAY" value="Color(0.46666667, 0.53333336, 0.6, 1)">
 			Light slate gray color.
 			Light slate gray color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_STEEL_BLUE" value="Color(0.690196, 0.768627, 0.870588, 1)">
+		<constant name="LIGHT_STEEL_BLUE" value="Color(0.6901961, 0.76862746, 0.87058824, 1)">
 			Light steel blue color.
 			Light steel blue color.
 		</constant>
 		</constant>
-		<constant name="LIGHT_YELLOW" value="Color(1, 1, 0.878431, 1)">
+		<constant name="LIGHT_YELLOW" value="Color(1, 1, 0.8784314, 1)">
 			Light yellow color.
 			Light yellow color.
 		</constant>
 		</constant>
 		<constant name="LIME" value="Color(0, 1, 0, 1)">
 		<constant name="LIME" value="Color(0, 1, 0, 1)">
 			Lime color.
 			Lime color.
 		</constant>
 		</constant>
-		<constant name="LIME_GREEN" value="Color(0.196078, 0.803922, 0.196078, 1)">
+		<constant name="LIME_GREEN" value="Color(0.19607843, 0.8039216, 0.19607843, 1)">
 			Lime green color.
 			Lime green color.
 		</constant>
 		</constant>
-		<constant name="LINEN" value="Color(0.980392, 0.941176, 0.901961, 1)">
+		<constant name="LINEN" value="Color(0.98039216, 0.9411765, 0.9019608, 1)">
 			Linen color.
 			Linen color.
 		</constant>
 		</constant>
 		<constant name="MAGENTA" value="Color(1, 0, 1, 1)">
 		<constant name="MAGENTA" value="Color(1, 0, 1, 1)">
 			Magenta color.
 			Magenta color.
 		</constant>
 		</constant>
-		<constant name="MAROON" value="Color(0.690196, 0.188235, 0.376471, 1)">
+		<constant name="MAROON" value="Color(0.6901961, 0.1882353, 0.3764706, 1)">
 			Maroon color.
 			Maroon color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_AQUAMARINE" value="Color(0.4, 0.803922, 0.666667, 1)">
+		<constant name="MEDIUM_AQUAMARINE" value="Color(0.4, 0.8039216, 0.6666667, 1)">
 			Medium aquamarine color.
 			Medium aquamarine color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_BLUE" value="Color(0, 0, 0.803922, 1)">
+		<constant name="MEDIUM_BLUE" value="Color(0, 0, 0.8039216, 1)">
 			Medium blue color.
 			Medium blue color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_ORCHID" value="Color(0.729412, 0.333333, 0.827451, 1)">
+		<constant name="MEDIUM_ORCHID" value="Color(0.7294118, 0.33333334, 0.827451, 1)">
 			Medium orchid color.
 			Medium orchid color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_PURPLE" value="Color(0.576471, 0.439216, 0.858824, 1)">
+		<constant name="MEDIUM_PURPLE" value="Color(0.5764706, 0.4392157, 0.85882354, 1)">
 			Medium purple color.
 			Medium purple color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_SEA_GREEN" value="Color(0.235294, 0.701961, 0.443137, 1)">
+		<constant name="MEDIUM_SEA_GREEN" value="Color(0.23529412, 0.7019608, 0.44313726, 1)">
 			Medium sea green color.
 			Medium sea green color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_SLATE_BLUE" value="Color(0.482353, 0.407843, 0.933333, 1)">
+		<constant name="MEDIUM_SLATE_BLUE" value="Color(0.48235294, 0.40784314, 0.93333334, 1)">
 			Medium slate blue color.
 			Medium slate blue color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_SPRING_GREEN" value="Color(0, 0.980392, 0.603922, 1)">
+		<constant name="MEDIUM_SPRING_GREEN" value="Color(0, 0.98039216, 0.6039216, 1)">
 			Medium spring green color.
 			Medium spring green color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_TURQUOISE" value="Color(0.282353, 0.819608, 0.8, 1)">
+		<constant name="MEDIUM_TURQUOISE" value="Color(0.28235295, 0.81960785, 0.8, 1)">
 			Medium turquoise color.
 			Medium turquoise color.
 		</constant>
 		</constant>
-		<constant name="MEDIUM_VIOLET_RED" value="Color(0.780392, 0.0823529, 0.521569, 1)">
+		<constant name="MEDIUM_VIOLET_RED" value="Color(0.78039217, 0.08235294, 0.52156866, 1)">
 			Medium violet red color.
 			Medium violet red color.
 		</constant>
 		</constant>
-		<constant name="MIDNIGHT_BLUE" value="Color(0.0980392, 0.0980392, 0.439216, 1)">
+		<constant name="MIDNIGHT_BLUE" value="Color(0.09803922, 0.09803922, 0.4392157, 1)">
 			Midnight blue color.
 			Midnight blue color.
 		</constant>
 		</constant>
-		<constant name="MINT_CREAM" value="Color(0.960784, 1, 0.980392, 1)">
+		<constant name="MINT_CREAM" value="Color(0.9607843, 1, 0.98039216, 1)">
 			Mint cream color.
 			Mint cream color.
 		</constant>
 		</constant>
-		<constant name="MISTY_ROSE" value="Color(1, 0.894118, 0.882353, 1)">
+		<constant name="MISTY_ROSE" value="Color(1, 0.89411765, 0.88235295, 1)">
 			Misty rose color.
 			Misty rose color.
 		</constant>
 		</constant>
-		<constant name="MOCCASIN" value="Color(1, 0.894118, 0.709804, 1)">
+		<constant name="MOCCASIN" value="Color(1, 0.89411765, 0.70980394, 1)">
 			Moccasin color.
 			Moccasin color.
 		</constant>
 		</constant>
-		<constant name="NAVAJO_WHITE" value="Color(1, 0.870588, 0.678431, 1)">
+		<constant name="NAVAJO_WHITE" value="Color(1, 0.87058824, 0.6784314, 1)">
 			Navajo white color.
 			Navajo white color.
 		</constant>
 		</constant>
-		<constant name="NAVY_BLUE" value="Color(0, 0, 0.501961, 1)">
+		<constant name="NAVY_BLUE" value="Color(0, 0, 0.5019608, 1)">
 			Navy blue color.
 			Navy blue color.
 		</constant>
 		</constant>
-		<constant name="OLD_LACE" value="Color(0.992157, 0.960784, 0.901961, 1)">
+		<constant name="OLD_LACE" value="Color(0.99215686, 0.9607843, 0.9019608, 1)">
 			Old lace color.
 			Old lace color.
 		</constant>
 		</constant>
-		<constant name="OLIVE" value="Color(0.501961, 0.501961, 0, 1)">
+		<constant name="OLIVE" value="Color(0.5019608, 0.5019608, 0, 1)">
 			Olive color.
 			Olive color.
 		</constant>
 		</constant>
-		<constant name="OLIVE_DRAB" value="Color(0.419608, 0.556863, 0.137255, 1)">
+		<constant name="OLIVE_DRAB" value="Color(0.41960785, 0.5568628, 0.13725491, 1)">
 			Olive drab color.
 			Olive drab color.
 		</constant>
 		</constant>
-		<constant name="ORANGE" value="Color(1, 0.647059, 0, 1)">
+		<constant name="ORANGE" value="Color(1, 0.64705884, 0, 1)">
 			Orange color.
 			Orange color.
 		</constant>
 		</constant>
-		<constant name="ORANGE_RED" value="Color(1, 0.270588, 0, 1)">
+		<constant name="ORANGE_RED" value="Color(1, 0.27058825, 0, 1)">
 			Orange red color.
 			Orange red color.
 		</constant>
 		</constant>
-		<constant name="ORCHID" value="Color(0.854902, 0.439216, 0.839216, 1)">
+		<constant name="ORCHID" value="Color(0.85490197, 0.4392157, 0.8392157, 1)">
 			Orchid color.
 			Orchid color.
 		</constant>
 		</constant>
-		<constant name="PALE_GOLDENROD" value="Color(0.933333, 0.909804, 0.666667, 1)">
+		<constant name="PALE_GOLDENROD" value="Color(0.93333334, 0.9098039, 0.6666667, 1)">
 			Pale goldenrod color.
 			Pale goldenrod color.
 		</constant>
 		</constant>
-		<constant name="PALE_GREEN" value="Color(0.596078, 0.984314, 0.596078, 1)">
+		<constant name="PALE_GREEN" value="Color(0.59607846, 0.9843137, 0.59607846, 1)">
 			Pale green color.
 			Pale green color.
 		</constant>
 		</constant>
-		<constant name="PALE_TURQUOISE" value="Color(0.686275, 0.933333, 0.933333, 1)">
+		<constant name="PALE_TURQUOISE" value="Color(0.6862745, 0.93333334, 0.93333334, 1)">
 			Pale turquoise color.
 			Pale turquoise color.
 		</constant>
 		</constant>
-		<constant name="PALE_VIOLET_RED" value="Color(0.858824, 0.439216, 0.576471, 1)">
+		<constant name="PALE_VIOLET_RED" value="Color(0.85882354, 0.4392157, 0.5764706, 1)">
 			Pale violet red color.
 			Pale violet red color.
 		</constant>
 		</constant>
-		<constant name="PAPAYA_WHIP" value="Color(1, 0.937255, 0.835294, 1)">
+		<constant name="PAPAYA_WHIP" value="Color(1, 0.9372549, 0.8352941, 1)">
 			Papaya whip color.
 			Papaya whip color.
 		</constant>
 		</constant>
-		<constant name="PEACH_PUFF" value="Color(1, 0.854902, 0.72549, 1)">
+		<constant name="PEACH_PUFF" value="Color(1, 0.85490197, 0.7254902, 1)">
 			Peach puff color.
 			Peach puff color.
 		</constant>
 		</constant>
-		<constant name="PERU" value="Color(0.803922, 0.521569, 0.247059, 1)">
+		<constant name="PERU" value="Color(0.8039216, 0.52156866, 0.24705882, 1)">
 			Peru color.
 			Peru color.
 		</constant>
 		</constant>
-		<constant name="PINK" value="Color(1, 0.752941, 0.796078, 1)">
+		<constant name="PINK" value="Color(1, 0.7529412, 0.79607844, 1)">
 			Pink color.
 			Pink color.
 		</constant>
 		</constant>
-		<constant name="PLUM" value="Color(0.866667, 0.627451, 0.866667, 1)">
+		<constant name="PLUM" value="Color(0.8666667, 0.627451, 0.8666667, 1)">
 			Plum color.
 			Plum color.
 		</constant>
 		</constant>
-		<constant name="POWDER_BLUE" value="Color(0.690196, 0.878431, 0.901961, 1)">
+		<constant name="POWDER_BLUE" value="Color(0.6901961, 0.8784314, 0.9019608, 1)">
 			Powder blue color.
 			Powder blue color.
 		</constant>
 		</constant>
-		<constant name="PURPLE" value="Color(0.627451, 0.12549, 0.941176, 1)">
+		<constant name="PURPLE" value="Color(0.627451, 0.1254902, 0.9411765, 1)">
 			Purple color.
 			Purple color.
 		</constant>
 		</constant>
 		<constant name="REBECCA_PURPLE" value="Color(0.4, 0.2, 0.6, 1)">
 		<constant name="REBECCA_PURPLE" value="Color(0.4, 0.2, 0.6, 1)">
@@ -883,97 +883,97 @@
 		<constant name="RED" value="Color(1, 0, 0, 1)">
 		<constant name="RED" value="Color(1, 0, 0, 1)">
 			Red color.
 			Red color.
 		</constant>
 		</constant>
-		<constant name="ROSY_BROWN" value="Color(0.737255, 0.560784, 0.560784, 1)">
+		<constant name="ROSY_BROWN" value="Color(0.7372549, 0.56078434, 0.56078434, 1)">
 			Rosy brown color.
 			Rosy brown color.
 		</constant>
 		</constant>
-		<constant name="ROYAL_BLUE" value="Color(0.254902, 0.411765, 0.882353, 1)">
+		<constant name="ROYAL_BLUE" value="Color(0.25490198, 0.4117647, 0.88235295, 1)">
 			Royal blue color.
 			Royal blue color.
 		</constant>
 		</constant>
-		<constant name="SADDLE_BROWN" value="Color(0.545098, 0.270588, 0.0745098, 1)">
+		<constant name="SADDLE_BROWN" value="Color(0.54509807, 0.27058825, 0.07450981, 1)">
 			Saddle brown color.
 			Saddle brown color.
 		</constant>
 		</constant>
-		<constant name="SALMON" value="Color(0.980392, 0.501961, 0.447059, 1)">
+		<constant name="SALMON" value="Color(0.98039216, 0.5019608, 0.44705883, 1)">
 			Salmon color.
 			Salmon color.
 		</constant>
 		</constant>
-		<constant name="SANDY_BROWN" value="Color(0.956863, 0.643137, 0.376471, 1)">
+		<constant name="SANDY_BROWN" value="Color(0.95686275, 0.6431373, 0.3764706, 1)">
 			Sandy brown color.
 			Sandy brown color.
 		</constant>
 		</constant>
-		<constant name="SEA_GREEN" value="Color(0.180392, 0.545098, 0.341176, 1)">
+		<constant name="SEA_GREEN" value="Color(0.18039216, 0.54509807, 0.34117648, 1)">
 			Sea green color.
 			Sea green color.
 		</constant>
 		</constant>
-		<constant name="SEASHELL" value="Color(1, 0.960784, 0.933333, 1)">
+		<constant name="SEASHELL" value="Color(1, 0.9607843, 0.93333334, 1)">
 			Seashell color.
 			Seashell color.
 		</constant>
 		</constant>
-		<constant name="SIENNA" value="Color(0.627451, 0.321569, 0.176471, 1)">
+		<constant name="SIENNA" value="Color(0.627451, 0.32156864, 0.1764706, 1)">
 			Sienna color.
 			Sienna color.
 		</constant>
 		</constant>
-		<constant name="SILVER" value="Color(0.752941, 0.752941, 0.752941, 1)">
+		<constant name="SILVER" value="Color(0.7529412, 0.7529412, 0.7529412, 1)">
 			Silver color.
 			Silver color.
 		</constant>
 		</constant>
-		<constant name="SKY_BLUE" value="Color(0.529412, 0.807843, 0.921569, 1)">
+		<constant name="SKY_BLUE" value="Color(0.5294118, 0.80784315, 0.92156863, 1)">
 			Sky blue color.
 			Sky blue color.
 		</constant>
 		</constant>
-		<constant name="SLATE_BLUE" value="Color(0.415686, 0.352941, 0.803922, 1)">
+		<constant name="SLATE_BLUE" value="Color(0.41568628, 0.3529412, 0.8039216, 1)">
 			Slate blue color.
 			Slate blue color.
 		</constant>
 		</constant>
-		<constant name="SLATE_GRAY" value="Color(0.439216, 0.501961, 0.564706, 1)">
+		<constant name="SLATE_GRAY" value="Color(0.4392157, 0.5019608, 0.5647059, 1)">
 			Slate gray color.
 			Slate gray color.
 		</constant>
 		</constant>
-		<constant name="SNOW" value="Color(1, 0.980392, 0.980392, 1)">
+		<constant name="SNOW" value="Color(1, 0.98039216, 0.98039216, 1)">
 			Snow color.
 			Snow color.
 		</constant>
 		</constant>
-		<constant name="SPRING_GREEN" value="Color(0, 1, 0.498039, 1)">
+		<constant name="SPRING_GREEN" value="Color(0, 1, 0.49803922, 1)">
 			Spring green color.
 			Spring green color.
 		</constant>
 		</constant>
-		<constant name="STEEL_BLUE" value="Color(0.27451, 0.509804, 0.705882, 1)">
+		<constant name="STEEL_BLUE" value="Color(0.27450982, 0.50980395, 0.7058824, 1)">
 			Steel blue color.
 			Steel blue color.
 		</constant>
 		</constant>
-		<constant name="TAN" value="Color(0.823529, 0.705882, 0.54902, 1)">
+		<constant name="TAN" value="Color(0.8235294, 0.7058824, 0.54901963, 1)">
 			Tan color.
 			Tan color.
 		</constant>
 		</constant>
-		<constant name="TEAL" value="Color(0, 0.501961, 0.501961, 1)">
+		<constant name="TEAL" value="Color(0, 0.5019608, 0.5019608, 1)">
 			Teal color.
 			Teal color.
 		</constant>
 		</constant>
-		<constant name="THISTLE" value="Color(0.847059, 0.74902, 0.847059, 1)">
+		<constant name="THISTLE" value="Color(0.84705883, 0.7490196, 0.84705883, 1)">
 			Thistle color.
 			Thistle color.
 		</constant>
 		</constant>
-		<constant name="TOMATO" value="Color(1, 0.388235, 0.278431, 1)">
+		<constant name="TOMATO" value="Color(1, 0.3882353, 0.2784314, 1)">
 			Tomato color.
 			Tomato color.
 		</constant>
 		</constant>
 		<constant name="TRANSPARENT" value="Color(1, 1, 1, 0)">
 		<constant name="TRANSPARENT" value="Color(1, 1, 1, 0)">
 			Transparent color (white with zero alpha).
 			Transparent color (white with zero alpha).
 		</constant>
 		</constant>
-		<constant name="TURQUOISE" value="Color(0.25098, 0.878431, 0.815686, 1)">
+		<constant name="TURQUOISE" value="Color(0.2509804, 0.8784314, 0.8156863, 1)">
 			Turquoise color.
 			Turquoise color.
 		</constant>
 		</constant>
-		<constant name="VIOLET" value="Color(0.933333, 0.509804, 0.933333, 1)">
+		<constant name="VIOLET" value="Color(0.93333334, 0.50980395, 0.93333334, 1)">
 			Violet color.
 			Violet color.
 		</constant>
 		</constant>
-		<constant name="WEB_GRAY" value="Color(0.501961, 0.501961, 0.501961, 1)">
+		<constant name="WEB_GRAY" value="Color(0.5019608, 0.5019608, 0.5019608, 1)">
 			Web gray color.
 			Web gray color.
 		</constant>
 		</constant>
-		<constant name="WEB_GREEN" value="Color(0, 0.501961, 0, 1)">
+		<constant name="WEB_GREEN" value="Color(0, 0.5019608, 0, 1)">
 			Web green color.
 			Web green color.
 		</constant>
 		</constant>
-		<constant name="WEB_MAROON" value="Color(0.501961, 0, 0, 1)">
+		<constant name="WEB_MAROON" value="Color(0.5019608, 0, 0, 1)">
 			Web maroon color.
 			Web maroon color.
 		</constant>
 		</constant>
-		<constant name="WEB_PURPLE" value="Color(0.501961, 0, 0.501961, 1)">
+		<constant name="WEB_PURPLE" value="Color(0.5019608, 0, 0.5019608, 1)">
 			Web purple color.
 			Web purple color.
 		</constant>
 		</constant>
-		<constant name="WHEAT" value="Color(0.960784, 0.870588, 0.701961, 1)">
+		<constant name="WHEAT" value="Color(0.9607843, 0.87058824, 0.7019608, 1)">
 			Wheat color.
 			Wheat color.
 		</constant>
 		</constant>
 		<constant name="WHITE" value="Color(1, 1, 1, 1)">
 		<constant name="WHITE" value="Color(1, 1, 1, 1)">
 			White color.
 			White color.
 		</constant>
 		</constant>
-		<constant name="WHITE_SMOKE" value="Color(0.960784, 0.960784, 0.960784, 1)">
+		<constant name="WHITE_SMOKE" value="Color(0.9607843, 0.9607843, 0.9607843, 1)">
 			White smoke color.
 			White smoke color.
 		</constant>
 		</constant>
 		<constant name="YELLOW" value="Color(1, 1, 0, 1)">
 		<constant name="YELLOW" value="Color(1, 1, 0, 1)">
 			Yellow color.
 			Yellow color.
 		</constant>
 		</constant>
-		<constant name="YELLOW_GREEN" value="Color(0.603922, 0.803922, 0.196078, 1)">
+		<constant name="YELLOW_GREEN" value="Color(0.6039216, 0.8039216, 0.19607843, 1)">
 			Yellow green color.
 			Yellow green color.
 		</constant>
 		</constant>
 	</constants>
 	</constants>

+ 2 - 2
doc/classes/ConeTwistJoint3D.xml

@@ -36,13 +36,13 @@
 		<member name="softness" type="float" setter="set_param" getter="get_param" default="0.8">
 		<member name="softness" type="float" setter="set_param" getter="get_param" default="0.8">
 			The ease with which the joint starts to twist. If it's too low, it takes more force to start twisting the joint.
 			The ease with which the joint starts to twist. If it's too low, it takes more force to start twisting the joint.
 		</member>
 		</member>
-		<member name="swing_span" type="float" setter="set_param" getter="get_param" default="0.785398">
+		<member name="swing_span" type="float" setter="set_param" getter="get_param" default="0.7853982">
 			Swing is rotation from side to side, around the axis perpendicular to the twist axis.
 			Swing is rotation from side to side, around the axis perpendicular to the twist axis.
 			The swing span defines, how much rotation will not get corrected along the swing axis.
 			The swing span defines, how much rotation will not get corrected along the swing axis.
 			Could be defined as looseness in the [ConeTwistJoint3D].
 			Could be defined as looseness in the [ConeTwistJoint3D].
 			If below 0.05, this behavior is locked.
 			If below 0.05, this behavior is locked.
 		</member>
 		</member>
-		<member name="twist_span" type="float" setter="set_param" getter="get_param" default="3.14159">
+		<member name="twist_span" type="float" setter="set_param" getter="get_param" default="3.1415927">
 			Twist is the rotation around the twist axis, this value defined how far the joint can twist.
 			Twist is the rotation around the twist axis, this value defined how far the joint can twist.
 			Twist is locked if below 0.05.
 			Twist is locked if below 0.05.
 		</member>
 		</member>

+ 2 - 2
doc/classes/GraphEdit.xml

@@ -397,10 +397,10 @@
 		<member name="zoom" type="float" setter="set_zoom" getter="get_zoom" default="1.0">
 		<member name="zoom" type="float" setter="set_zoom" getter="get_zoom" default="1.0">
 			The current zoom value.
 			The current zoom value.
 		</member>
 		</member>
-		<member name="zoom_max" type="float" setter="set_zoom_max" getter="get_zoom_max" default="2.0736">
+		<member name="zoom_max" type="float" setter="set_zoom_max" getter="get_zoom_max" default="2.0736003">
 			The upper zoom limit.
 			The upper zoom limit.
 		</member>
 		</member>
-		<member name="zoom_min" type="float" setter="set_zoom_min" getter="get_zoom_min" default="0.232568">
+		<member name="zoom_min" type="float" setter="set_zoom_min" getter="get_zoom_min" default="0.23256795">
 			The lower zoom limit.
 			The lower zoom limit.
 		</member>
 		</member>
 		<member name="zoom_step" type="float" setter="set_zoom_step" getter="get_zoom_step" default="1.2">
 		<member name="zoom_step" type="float" setter="set_zoom_step" getter="get_zoom_step" default="1.2">

+ 2 - 2
doc/classes/HingeJoint3D.xml

@@ -47,7 +47,7 @@
 		<member name="angular_limit/enable" type="bool" setter="set_flag" getter="get_flag" default="false">
 		<member name="angular_limit/enable" type="bool" setter="set_flag" getter="get_flag" default="false">
 			If [code]true[/code], the hinges maximum and minimum rotation, defined by [member angular_limit/lower] and [member angular_limit/upper] has effects.
 			If [code]true[/code], the hinges maximum and minimum rotation, defined by [member angular_limit/lower] and [member angular_limit/upper] has effects.
 		</member>
 		</member>
-		<member name="angular_limit/lower" type="float" setter="set_param" getter="get_param" default="-1.5708">
+		<member name="angular_limit/lower" type="float" setter="set_param" getter="get_param" default="-1.5707964">
 			The minimum rotation. Only active if [member angular_limit/enable] is [code]true[/code].
 			The minimum rotation. Only active if [member angular_limit/enable] is [code]true[/code].
 		</member>
 		</member>
 		<member name="angular_limit/relaxation" type="float" setter="set_param" getter="get_param" default="1.0">
 		<member name="angular_limit/relaxation" type="float" setter="set_param" getter="get_param" default="1.0">
@@ -55,7 +55,7 @@
 		</member>
 		</member>
 		<member name="angular_limit/softness" type="float" setter="set_param" getter="get_param" default="0.9" deprecated="This property is never set by the engine and is kept for compatibility purposes.">
 		<member name="angular_limit/softness" type="float" setter="set_param" getter="get_param" default="0.9" deprecated="This property is never set by the engine and is kept for compatibility purposes.">
 		</member>
 		</member>
-		<member name="angular_limit/upper" type="float" setter="set_param" getter="get_param" default="1.5708">
+		<member name="angular_limit/upper" type="float" setter="set_param" getter="get_param" default="1.5707964">
 			The maximum rotation. Only active if [member angular_limit/enable] is [code]true[/code].
 			The maximum rotation. Only active if [member angular_limit/enable] is [code]true[/code].
 		</member>
 		</member>
 		<member name="motor/enable" type="bool" setter="set_flag" getter="get_flag" default="false">
 		<member name="motor/enable" type="bool" setter="set_flag" getter="get_flag" default="false">

+ 2 - 2
doc/classes/Parallax2D.xml

@@ -20,10 +20,10 @@
 		<member name="ignore_camera_scroll" type="bool" setter="set_ignore_camera_scroll" getter="is_ignore_camera_scroll" default="false">
 		<member name="ignore_camera_scroll" type="bool" setter="set_ignore_camera_scroll" getter="is_ignore_camera_scroll" default="false">
 			If [code]true[/code], [Parallax2D]'s position is not affected by the position of the camera.
 			If [code]true[/code], [Parallax2D]'s position is not affected by the position of the camera.
 		</member>
 		</member>
-		<member name="limit_begin" type="Vector2" setter="set_limit_begin" getter="get_limit_begin" default="Vector2(-1e+07, -1e+07)">
+		<member name="limit_begin" type="Vector2" setter="set_limit_begin" getter="get_limit_begin" default="Vector2(-10000000, -10000000)">
 			Top-left limits for scrolling to begin. If the camera is outside of this limit, the [Parallax2D] stops scrolling. Must be lower than [member limit_end] minus the viewport size to work.
 			Top-left limits for scrolling to begin. If the camera is outside of this limit, the [Parallax2D] stops scrolling. Must be lower than [member limit_end] minus the viewport size to work.
 		</member>
 		</member>
-		<member name="limit_end" type="Vector2" setter="set_limit_end" getter="get_limit_end" default="Vector2(1e+07, 1e+07)">
+		<member name="limit_end" type="Vector2" setter="set_limit_end" getter="get_limit_end" default="Vector2(10000000, 10000000)">
 			Bottom-right limits for scrolling to end. If the camera is outside of this limit, the [Parallax2D] will stop scrolling. Must be higher than [member limit_begin] and the viewport size combined to work.
 			Bottom-right limits for scrolling to end. If the camera is outside of this limit, the [Parallax2D] will stop scrolling. Must be higher than [member limit_begin] and the viewport size combined to work.
 		</member>
 		</member>
 		<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="2" />
 		<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="2" />

+ 5 - 5
doc/classes/ProjectSettings.xml

@@ -2403,7 +2403,7 @@
 		<member name="physics/2d/run_on_separate_thread" type="bool" setter="" getter="" default="false">
 		<member name="physics/2d/run_on_separate_thread" type="bool" setter="" getter="" default="false">
 			If [code]true[/code], the 2D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 2D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process.
 			If [code]true[/code], the 2D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 2D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process.
 		</member>
 		</member>
-		<member name="physics/2d/sleep_threshold_angular" type="float" setter="" getter="" default="0.139626">
+		<member name="physics/2d/sleep_threshold_angular" type="float" setter="" getter="" default="0.13962634">
 			Threshold angular velocity under which a 2D physics body will be considered inactive. See [constant PhysicsServer2D.SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD].
 			Threshold angular velocity under which a 2D physics body will be considered inactive. See [constant PhysicsServer2D.SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD].
 		</member>
 		</member>
 		<member name="physics/2d/sleep_threshold_linear" type="float" setter="" getter="" default="2.0">
 		<member name="physics/2d/sleep_threshold_linear" type="float" setter="" getter="" default="2.0">
@@ -2486,7 +2486,7 @@
 			If [code]true[/code], the 3D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 3D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process.
 			If [code]true[/code], the 3D physics server runs on a separate thread, making better use of multi-core CPUs. If [code]false[/code], the 3D physics server runs on the main thread. Running the physics server on a separate thread can increase performance, but restricts API access to only physics process.
 			[b]Note:[/b] When [member physics/3d/physics_engine] is set to [code]Jolt Physics[/code], enabling this setting will prevent the 3D physics server from being able to provide any context when reporting errors and warnings, and will instead always refer to nodes as [code]&lt;unknown&gt;[/code].
 			[b]Note:[/b] When [member physics/3d/physics_engine] is set to [code]Jolt Physics[/code], enabling this setting will prevent the 3D physics server from being able to provide any context when reporting errors and warnings, and will instead always refer to nodes as [code]&lt;unknown&gt;[/code].
 		</member>
 		</member>
-		<member name="physics/3d/sleep_threshold_angular" type="float" setter="" getter="" default="0.139626">
+		<member name="physics/3d/sleep_threshold_angular" type="float" setter="" getter="" default="0.13962634">
 			Threshold angular velocity under which a 3D physics body will be considered inactive. See [constant PhysicsServer3D.SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD].
 			Threshold angular velocity under which a 3D physics body will be considered inactive. See [constant PhysicsServer3D.SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_THRESHOLD].
 		</member>
 		</member>
 		<member name="physics/3d/sleep_threshold_linear" type="float" setter="" getter="" default="0.1">
 		<member name="physics/3d/sleep_threshold_linear" type="float" setter="" getter="" default="0.1">
@@ -2535,7 +2535,7 @@
 			[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead.
 			[b]Note:[/b] This property is only read when the project starts. To change the physics FPS at runtime, set [member Engine.physics_ticks_per_second] instead.
 			[b]Note:[/b] Only [member physics/common/max_physics_steps_per_frame] physics ticks may be simulated per rendered frame at most. If more physics ticks have to be simulated per rendered frame to keep up with rendering, the project will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended to also increase [member physics/common/max_physics_steps_per_frame] if increasing [member physics/common/physics_ticks_per_second] significantly above its default value.
 			[b]Note:[/b] Only [member physics/common/max_physics_steps_per_frame] physics ticks may be simulated per rendered frame at most. If more physics ticks have to be simulated per rendered frame to keep up with rendering, the project will appear to slow down (even if [code]delta[/code] is used consistently in physics calculations). Therefore, it is recommended to also increase [member physics/common/max_physics_steps_per_frame] if increasing [member physics/common/physics_ticks_per_second] significantly above its default value.
 		</member>
 		</member>
-		<member name="physics/jolt_physics_3d/collisions/active_edge_threshold" type="float" setter="" getter="" default="0.872665">
+		<member name="physics/jolt_physics_3d/collisions/active_edge_threshold" type="float" setter="" getter="" default="0.87266463">
 			The maximum angle, in radians, between two adjacent triangles in a [ConcavePolygonShape3D] or [HeightMapShape3D] for which the edge between those triangles is considered inactive.
 			The maximum angle, in radians, between two adjacent triangles in a [ConcavePolygonShape3D] or [HeightMapShape3D] for which the edge between those triangles is considered inactive.
 			Collisions against an inactive edge will have its normal overridden to instead be the surface normal of the triangle. This can help alleviate ghost collisions.
 			Collisions against an inactive edge will have its normal overridden to instead be the surface normal of the triangle. This can help alleviate ghost collisions.
 			[b]Note:[/b] Setting this too high can result in objects not depenetrating properly.
 			[b]Note:[/b] Setting this too high can result in objects not depenetrating properly.
@@ -2551,7 +2551,7 @@
 			Which of the two nodes bound by a joint should represent the world when one of the two is omitted, as either [member Joint3D.node_a] or [member Joint3D.node_b]. This can be thought of as having the omitted node be a [StaticBody3D] at the joint's position. Joint limits are more easily expressed when [member Joint3D.node_a] represents the world.
 			Which of the two nodes bound by a joint should represent the world when one of the two is omitted, as either [member Joint3D.node_a] or [member Joint3D.node_b]. This can be thought of as having the omitted node be a [StaticBody3D] at the joint's position. Joint limits are more easily expressed when [member Joint3D.node_a] represents the world.
 			[b]Note:[/b] In Godot Physics, only [member Joint3D.node_b] can represent the world.
 			[b]Note:[/b] In Godot Physics, only [member Joint3D.node_b] can represent the world.
 		</member>
 		</member>
-		<member name="physics/jolt_physics_3d/limits/max_angular_velocity" type="float" setter="" getter="" default="47.1239">
+		<member name="physics/jolt_physics_3d/limits/max_angular_velocity" type="float" setter="" getter="" default="47.12389">
 			The maximum angular velocity that a [RigidBody3D] can reach, in radians per second.
 			The maximum angular velocity that a [RigidBody3D] can reach, in radians per second.
 			This is mainly used as a fail-safe, to prevent the simulation from exploding, as fast-moving objects colliding with complex physics structures can otherwise cause them to go out of control. Fast-moving objects can also cause a lot of stress on the collision detection system, which can slow down the simulation considerably.
 			This is mainly used as a fail-safe, to prevent the simulation from exploding, as fast-moving objects colliding with complex physics structures can otherwise cause them to go out of control. Fast-moving objects can also cause a lot of stress on the collision detection system, which can slow down the simulation considerably.
 		</member>
 		</member>
@@ -2611,7 +2611,7 @@
 			How much of the position error of a [RigidBody3D] to fix during a physics step, where [code]0.0[/code] is none and [code]1.0[/code] is the full amount. This affects things like how quickly bodies depenetrate.
 			How much of the position error of a [RigidBody3D] to fix during a physics step, where [code]0.0[/code] is none and [code]1.0[/code] is the full amount. This affects things like how quickly bodies depenetrate.
 			[b]Note:[/b] Setting this value too high can make [RigidBody3D] nodes unstable.
 			[b]Note:[/b] Setting this value too high can make [RigidBody3D] nodes unstable.
 		</member>
 		</member>
-		<member name="physics/jolt_physics_3d/simulation/body_pair_contact_cache_angle_threshold" type="float" setter="" getter="" default="0.0349066">
+		<member name="physics/jolt_physics_3d/simulation/body_pair_contact_cache_angle_threshold" type="float" setter="" getter="" default="0.034906585">
 			The maximum relative angle by which a body pair can move and still reuse the collision results from the previous physics step, in radians.
 			The maximum relative angle by which a body pair can move and still reuse the collision results from the previous physics step, in radians.
 		</member>
 		</member>
 		<member name="physics/jolt_physics_3d/simulation/body_pair_contact_cache_distance_threshold" type="float" setter="" getter="" default="0.001">
 		<member name="physics/jolt_physics_3d/simulation/body_pair_contact_cache_distance_threshold" type="float" setter="" getter="" default="0.001">

+ 2 - 1
editor/doc_tools.cpp

@@ -33,6 +33,7 @@
 #include "core/config/engine.h"
 #include "core/config/engine.h"
 #include "core/config/project_settings.h"
 #include "core/config/project_settings.h"
 #include "core/core_constants.h"
 #include "core/core_constants.h"
+#include "core/doc_data.h"
 #include "core/io/compression.h"
 #include "core/io/compression.h"
 #include "core/io/dir_access.h"
 #include "core/io/dir_access.h"
 #include "core/io/resource_importer.h"
 #include "core/io/resource_importer.h"
@@ -930,7 +931,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
 			DocData::ConstantDoc constant;
 			DocData::ConstantDoc constant;
 			constant.name = E;
 			constant.name = E;
 			Variant value = Variant::get_constant_value(Variant::Type(i), E);
 			Variant value = Variant::get_constant_value(Variant::Type(i), E);
-			constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string().replace_char('\n', ' ');
+			constant.value = DocData::get_default_value_string(value);
 			constant.is_value_valid = true;
 			constant.is_value_valid = true;
 			constant.type = Variant::get_type_name(value.get_type());
 			constant.type = Variant::get_type_name(value.get_type());
 			c.constants.push_back(constant);
 			c.constants.push_back(constant);

+ 140 - 0
misc/extension_api_validation/4.4-stable.expected

@@ -111,3 +111,143 @@ Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/cre
 Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/create_action/arguments': size changed value in new API, from 3 to 5.
 Validate extension JSON: Error: Field 'classes/EditorUndoRedoManager/methods/create_action/arguments': size changed value in new API, from 3 to 5.
 
 
 New argument added. Compatibility method registered.
 New argument added. Compatibility method registered.
+
+
+GH-98750
+--------
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ALICE_BLUE': value changed value in new API, from "Color(0.941176, 0.972549, 1, 1)" to "Color(0.9411765, 0.972549, 1, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ANTIQUE_WHITE': value changed value in new API, from "Color(0.980392, 0.921569, 0.843137, 1)" to "Color(0.98039216, 0.92156863, 0.84313726, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/AQUAMARINE': value changed value in new API, from "Color(0.498039, 1, 0.831373, 1)" to "Color(0.49803922, 1, 0.83137256, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/AZURE': value changed value in new API, from "Color(0.941176, 1, 1, 1)" to "Color(0.9411765, 1, 1, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/BEIGE': value changed value in new API, from "Color(0.960784, 0.960784, 0.862745, 1)" to "Color(0.9607843, 0.9607843, 0.8627451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/BISQUE': value changed value in new API, from "Color(1, 0.894118, 0.768627, 1)" to "Color(1, 0.89411765, 0.76862746, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/BLANCHED_ALMOND': value changed value in new API, from "Color(1, 0.921569, 0.803922, 1)" to "Color(1, 0.92156863, 0.8039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/BLUE_VIOLET': value changed value in new API, from "Color(0.541176, 0.168627, 0.886275, 1)" to "Color(0.5411765, 0.16862746, 0.8862745, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/BROWN': value changed value in new API, from "Color(0.647059, 0.164706, 0.164706, 1)" to "Color(0.64705884, 0.16470589, 0.16470589, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/BURLYWOOD': value changed value in new API, from "Color(0.870588, 0.721569, 0.529412, 1)" to "Color(0.87058824, 0.72156864, 0.5294118, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CADET_BLUE': value changed value in new API, from "Color(0.372549, 0.619608, 0.627451, 1)" to "Color(0.37254903, 0.61960787, 0.627451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CHARTREUSE': value changed value in new API, from "Color(0.498039, 1, 0, 1)" to "Color(0.49803922, 1, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CHOCOLATE': value changed value in new API, from "Color(0.823529, 0.411765, 0.117647, 1)" to "Color(0.8235294, 0.4117647, 0.11764706, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CORAL': value changed value in new API, from "Color(1, 0.498039, 0.313726, 1)" to "Color(1, 0.49803922, 0.3137255, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CORNFLOWER_BLUE': value changed value in new API, from "Color(0.392157, 0.584314, 0.929412, 1)" to "Color(0.39215687, 0.58431375, 0.92941177, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CORNSILK': value changed value in new API, from "Color(1, 0.972549, 0.862745, 1)" to "Color(1, 0.972549, 0.8627451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/CRIMSON': value changed value in new API, from "Color(0.862745, 0.0784314, 0.235294, 1)" to "Color(0.8627451, 0.078431375, 0.23529412, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_BLUE': value changed value in new API, from "Color(0, 0, 0.545098, 1)" to "Color(0, 0, 0.54509807, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_CYAN': value changed value in new API, from "Color(0, 0.545098, 0.545098, 1)" to "Color(0, 0.54509807, 0.54509807, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_GOLDENROD': value changed value in new API, from "Color(0.721569, 0.52549, 0.0431373, 1)" to "Color(0.72156864, 0.5254902, 0.043137256, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_GRAY': value changed value in new API, from "Color(0.662745, 0.662745, 0.662745, 1)" to "Color(0.6627451, 0.6627451, 0.6627451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_GREEN': value changed value in new API, from "Color(0, 0.392157, 0, 1)" to "Color(0, 0.39215687, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_KHAKI': value changed value in new API, from "Color(0.741176, 0.717647, 0.419608, 1)" to "Color(0.7411765, 0.7176471, 0.41960785, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_MAGENTA': value changed value in new API, from "Color(0.545098, 0, 0.545098, 1)" to "Color(0.54509807, 0, 0.54509807, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_OLIVE_GREEN': value changed value in new API, from "Color(0.333333, 0.419608, 0.184314, 1)" to "Color(0.33333334, 0.41960785, 0.18431373, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_ORANGE': value changed value in new API, from "Color(1, 0.54902, 0, 1)" to "Color(1, 0.54901963, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_ORCHID': value changed value in new API, from "Color(0.6, 0.196078, 0.8, 1)" to "Color(0.6, 0.19607843, 0.8, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_RED': value changed value in new API, from "Color(0.545098, 0, 0, 1)" to "Color(0.54509807, 0, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_SALMON': value changed value in new API, from "Color(0.913725, 0.588235, 0.478431, 1)" to "Color(0.9137255, 0.5882353, 0.47843137, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_SEA_GREEN': value changed value in new API, from "Color(0.560784, 0.737255, 0.560784, 1)" to "Color(0.56078434, 0.7372549, 0.56078434, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_SLATE_BLUE': value changed value in new API, from "Color(0.282353, 0.239216, 0.545098, 1)" to "Color(0.28235295, 0.23921569, 0.54509807, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_SLATE_GRAY': value changed value in new API, from "Color(0.184314, 0.309804, 0.309804, 1)" to "Color(0.18431373, 0.30980393, 0.30980393, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_TURQUOISE': value changed value in new API, from "Color(0, 0.807843, 0.819608, 1)" to "Color(0, 0.80784315, 0.81960785, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DARK_VIOLET': value changed value in new API, from "Color(0.580392, 0, 0.827451, 1)" to "Color(0.5803922, 0, 0.827451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DEEP_PINK': value changed value in new API, from "Color(1, 0.0784314, 0.576471, 1)" to "Color(1, 0.078431375, 0.5764706, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DEEP_SKY_BLUE': value changed value in new API, from "Color(0, 0.74902, 1, 1)" to "Color(0, 0.7490196, 1, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DIM_GRAY': value changed value in new API, from "Color(0.411765, 0.411765, 0.411765, 1)" to "Color(0.4117647, 0.4117647, 0.4117647, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/DODGER_BLUE': value changed value in new API, from "Color(0.117647, 0.564706, 1, 1)" to "Color(0.11764706, 0.5647059, 1, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/FIREBRICK': value changed value in new API, from "Color(0.698039, 0.133333, 0.133333, 1)" to "Color(0.69803923, 0.13333334, 0.13333334, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/FLORAL_WHITE': value changed value in new API, from "Color(1, 0.980392, 0.941176, 1)" to "Color(1, 0.98039216, 0.9411765, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/FOREST_GREEN': value changed value in new API, from "Color(0.133333, 0.545098, 0.133333, 1)" to "Color(0.13333334, 0.54509807, 0.13333334, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/GAINSBORO': value changed value in new API, from "Color(0.862745, 0.862745, 0.862745, 1)" to "Color(0.8627451, 0.8627451, 0.8627451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/GOLD': value changed value in new API, from "Color(1, 0.843137, 0, 1)" to "Color(1, 0.84313726, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/GOLDENROD': value changed value in new API, from "Color(0.854902, 0.647059, 0.12549, 1)" to "Color(0.85490197, 0.64705884, 0.1254902, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/GRAY': value changed value in new API, from "Color(0.745098, 0.745098, 0.745098, 1)" to "Color(0.74509805, 0.74509805, 0.74509805, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/GREEN_YELLOW': value changed value in new API, from "Color(0.678431, 1, 0.184314, 1)" to "Color(0.6784314, 1, 0.18431373, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/HONEYDEW': value changed value in new API, from "Color(0.941176, 1, 0.941176, 1)" to "Color(0.9411765, 1, 0.9411765, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/HOT_PINK': value changed value in new API, from "Color(1, 0.411765, 0.705882, 1)" to "Color(1, 0.4117647, 0.7058824, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/INDIAN_RED': value changed value in new API, from "Color(0.803922, 0.360784, 0.360784, 1)" to "Color(0.8039216, 0.36078432, 0.36078432, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/INDIGO': value changed value in new API, from "Color(0.294118, 0, 0.509804, 1)" to "Color(0.29411766, 0, 0.50980395, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/IVORY': value changed value in new API, from "Color(1, 1, 0.941176, 1)" to "Color(1, 1, 0.9411765, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/KHAKI': value changed value in new API, from "Color(0.941176, 0.901961, 0.54902, 1)" to "Color(0.9411765, 0.9019608, 0.54901963, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LAVENDER': value changed value in new API, from "Color(0.901961, 0.901961, 0.980392, 1)" to "Color(0.9019608, 0.9019608, 0.98039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LAVENDER_BLUSH': value changed value in new API, from "Color(1, 0.941176, 0.960784, 1)" to "Color(1, 0.9411765, 0.9607843, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LAWN_GREEN': value changed value in new API, from "Color(0.486275, 0.988235, 0, 1)" to "Color(0.4862745, 0.9882353, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LEMON_CHIFFON': value changed value in new API, from "Color(1, 0.980392, 0.803922, 1)" to "Color(1, 0.98039216, 0.8039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_BLUE': value changed value in new API, from "Color(0.678431, 0.847059, 0.901961, 1)" to "Color(0.6784314, 0.84705883, 0.9019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_CORAL': value changed value in new API, from "Color(0.941176, 0.501961, 0.501961, 1)" to "Color(0.9411765, 0.5019608, 0.5019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_CYAN': value changed value in new API, from "Color(0.878431, 1, 1, 1)" to "Color(0.8784314, 1, 1, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_GOLDENROD': value changed value in new API, from "Color(0.980392, 0.980392, 0.823529, 1)" to "Color(0.98039216, 0.98039216, 0.8235294, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_GREEN': value changed value in new API, from "Color(0.564706, 0.933333, 0.564706, 1)" to "Color(0.5647059, 0.93333334, 0.5647059, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_PINK': value changed value in new API, from "Color(1, 0.713726, 0.756863, 1)" to "Color(1, 0.7137255, 0.75686276, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_SALMON': value changed value in new API, from "Color(1, 0.627451, 0.478431, 1)" to "Color(1, 0.627451, 0.47843137, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_SEA_GREEN': value changed value in new API, from "Color(0.12549, 0.698039, 0.666667, 1)" to "Color(0.1254902, 0.69803923, 0.6666667, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_SKY_BLUE': value changed value in new API, from "Color(0.529412, 0.807843, 0.980392, 1)" to "Color(0.5294118, 0.80784315, 0.98039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_SLATE_GRAY': value changed value in new API, from "Color(0.466667, 0.533333, 0.6, 1)" to "Color(0.46666667, 0.53333336, 0.6, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_STEEL_BLUE': value changed value in new API, from "Color(0.690196, 0.768627, 0.870588, 1)" to "Color(0.6901961, 0.76862746, 0.87058824, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIGHT_YELLOW': value changed value in new API, from "Color(1, 1, 0.878431, 1)" to "Color(1, 1, 0.8784314, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LIME_GREEN': value changed value in new API, from "Color(0.196078, 0.803922, 0.196078, 1)" to "Color(0.19607843, 0.8039216, 0.19607843, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/LINEN': value changed value in new API, from "Color(0.980392, 0.941176, 0.901961, 1)" to "Color(0.98039216, 0.9411765, 0.9019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MAROON': value changed value in new API, from "Color(0.690196, 0.188235, 0.376471, 1)" to "Color(0.6901961, 0.1882353, 0.3764706, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_AQUAMARINE': value changed value in new API, from "Color(0.4, 0.803922, 0.666667, 1)" to "Color(0.4, 0.8039216, 0.6666667, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_BLUE': value changed value in new API, from "Color(0, 0, 0.803922, 1)" to "Color(0, 0, 0.8039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_ORCHID': value changed value in new API, from "Color(0.729412, 0.333333, 0.827451, 1)" to "Color(0.7294118, 0.33333334, 0.827451, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_PURPLE': value changed value in new API, from "Color(0.576471, 0.439216, 0.858824, 1)" to "Color(0.5764706, 0.4392157, 0.85882354, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_SEA_GREEN': value changed value in new API, from "Color(0.235294, 0.701961, 0.443137, 1)" to "Color(0.23529412, 0.7019608, 0.44313726, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_SLATE_BLUE': value changed value in new API, from "Color(0.482353, 0.407843, 0.933333, 1)" to "Color(0.48235294, 0.40784314, 0.93333334, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_SPRING_GREEN': value changed value in new API, from "Color(0, 0.980392, 0.603922, 1)" to "Color(0, 0.98039216, 0.6039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_TURQUOISE': value changed value in new API, from "Color(0.282353, 0.819608, 0.8, 1)" to "Color(0.28235295, 0.81960785, 0.8, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MEDIUM_VIOLET_RED': value changed value in new API, from "Color(0.780392, 0.0823529, 0.521569, 1)" to "Color(0.78039217, 0.08235294, 0.52156866, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MIDNIGHT_BLUE': value changed value in new API, from "Color(0.0980392, 0.0980392, 0.439216, 1)" to "Color(0.09803922, 0.09803922, 0.4392157, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MINT_CREAM': value changed value in new API, from "Color(0.960784, 1, 0.980392, 1)" to "Color(0.9607843, 1, 0.98039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MISTY_ROSE': value changed value in new API, from "Color(1, 0.894118, 0.882353, 1)" to "Color(1, 0.89411765, 0.88235295, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/MOCCASIN': value changed value in new API, from "Color(1, 0.894118, 0.709804, 1)" to "Color(1, 0.89411765, 0.70980394, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/NAVAJO_WHITE': value changed value in new API, from "Color(1, 0.870588, 0.678431, 1)" to "Color(1, 0.87058824, 0.6784314, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/NAVY_BLUE': value changed value in new API, from "Color(0, 0, 0.501961, 1)" to "Color(0, 0, 0.5019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/OLD_LACE': value changed value in new API, from "Color(0.992157, 0.960784, 0.901961, 1)" to "Color(0.99215686, 0.9607843, 0.9019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/OLIVE': value changed value in new API, from "Color(0.501961, 0.501961, 0, 1)" to "Color(0.5019608, 0.5019608, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/OLIVE_DRAB': value changed value in new API, from "Color(0.419608, 0.556863, 0.137255, 1)" to "Color(0.41960785, 0.5568628, 0.13725491, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ORANGE': value changed value in new API, from "Color(1, 0.647059, 0, 1)" to "Color(1, 0.64705884, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ORANGE_RED': value changed value in new API, from "Color(1, 0.270588, 0, 1)" to "Color(1, 0.27058825, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ORCHID': value changed value in new API, from "Color(0.854902, 0.439216, 0.839216, 1)" to "Color(0.85490197, 0.4392157, 0.8392157, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PALE_GOLDENROD': value changed value in new API, from "Color(0.933333, 0.909804, 0.666667, 1)" to "Color(0.93333334, 0.9098039, 0.6666667, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PALE_GREEN': value changed value in new API, from "Color(0.596078, 0.984314, 0.596078, 1)" to "Color(0.59607846, 0.9843137, 0.59607846, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PALE_TURQUOISE': value changed value in new API, from "Color(0.686275, 0.933333, 0.933333, 1)" to "Color(0.6862745, 0.93333334, 0.93333334, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PALE_VIOLET_RED': value changed value in new API, from "Color(0.858824, 0.439216, 0.576471, 1)" to "Color(0.85882354, 0.4392157, 0.5764706, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PAPAYA_WHIP': value changed value in new API, from "Color(1, 0.937255, 0.835294, 1)" to "Color(1, 0.9372549, 0.8352941, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PEACH_PUFF': value changed value in new API, from "Color(1, 0.854902, 0.72549, 1)" to "Color(1, 0.85490197, 0.7254902, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PERU': value changed value in new API, from "Color(0.803922, 0.521569, 0.247059, 1)" to "Color(0.8039216, 0.52156866, 0.24705882, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PINK': value changed value in new API, from "Color(1, 0.752941, 0.796078, 1)" to "Color(1, 0.7529412, 0.79607844, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PLUM': value changed value in new API, from "Color(0.866667, 0.627451, 0.866667, 1)" to "Color(0.8666667, 0.627451, 0.8666667, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/POWDER_BLUE': value changed value in new API, from "Color(0.690196, 0.878431, 0.901961, 1)" to "Color(0.6901961, 0.8784314, 0.9019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/PURPLE': value changed value in new API, from "Color(0.627451, 0.12549, 0.941176, 1)" to "Color(0.627451, 0.1254902, 0.9411765, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ROSY_BROWN': value changed value in new API, from "Color(0.737255, 0.560784, 0.560784, 1)" to "Color(0.7372549, 0.56078434, 0.56078434, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/ROYAL_BLUE': value changed value in new API, from "Color(0.254902, 0.411765, 0.882353, 1)" to "Color(0.25490198, 0.4117647, 0.88235295, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SADDLE_BROWN': value changed value in new API, from "Color(0.545098, 0.270588, 0.0745098, 1)" to "Color(0.54509807, 0.27058825, 0.07450981, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SALMON': value changed value in new API, from "Color(0.980392, 0.501961, 0.447059, 1)" to "Color(0.98039216, 0.5019608, 0.44705883, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SANDY_BROWN': value changed value in new API, from "Color(0.956863, 0.643137, 0.376471, 1)" to "Color(0.95686275, 0.6431373, 0.3764706, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SEASHELL': value changed value in new API, from "Color(1, 0.960784, 0.933333, 1)" to "Color(1, 0.9607843, 0.93333334, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SEA_GREEN': value changed value in new API, from "Color(0.180392, 0.545098, 0.341176, 1)" to "Color(0.18039216, 0.54509807, 0.34117648, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SIENNA': value changed value in new API, from "Color(0.627451, 0.321569, 0.176471, 1)" to "Color(0.627451, 0.32156864, 0.1764706, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SILVER': value changed value in new API, from "Color(0.752941, 0.752941, 0.752941, 1)" to "Color(0.7529412, 0.7529412, 0.7529412, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SKY_BLUE': value changed value in new API, from "Color(0.529412, 0.807843, 0.921569, 1)" to "Color(0.5294118, 0.80784315, 0.92156863, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SLATE_BLUE': value changed value in new API, from "Color(0.415686, 0.352941, 0.803922, 1)" to "Color(0.41568628, 0.3529412, 0.8039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SLATE_GRAY': value changed value in new API, from "Color(0.439216, 0.501961, 0.564706, 1)" to "Color(0.4392157, 0.5019608, 0.5647059, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SNOW': value changed value in new API, from "Color(1, 0.980392, 0.980392, 1)" to "Color(1, 0.98039216, 0.98039216, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/SPRING_GREEN': value changed value in new API, from "Color(0, 1, 0.498039, 1)" to "Color(0, 1, 0.49803922, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/STEEL_BLUE': value changed value in new API, from "Color(0.27451, 0.509804, 0.705882, 1)" to "Color(0.27450982, 0.50980395, 0.7058824, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/TAN': value changed value in new API, from "Color(0.823529, 0.705882, 0.54902, 1)" to "Color(0.8235294, 0.7058824, 0.54901963, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/TEAL': value changed value in new API, from "Color(0, 0.501961, 0.501961, 1)" to "Color(0, 0.5019608, 0.5019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/THISTLE': value changed value in new API, from "Color(0.847059, 0.74902, 0.847059, 1)" to "Color(0.84705883, 0.7490196, 0.84705883, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/TOMATO': value changed value in new API, from "Color(1, 0.388235, 0.278431, 1)" to "Color(1, 0.3882353, 0.2784314, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/TURQUOISE': value changed value in new API, from "Color(0.25098, 0.878431, 0.815686, 1)" to "Color(0.2509804, 0.8784314, 0.8156863, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/VIOLET': value changed value in new API, from "Color(0.933333, 0.509804, 0.933333, 1)" to "Color(0.93333334, 0.50980395, 0.93333334, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/WEB_GRAY': value changed value in new API, from "Color(0.501961, 0.501961, 0.501961, 1)" to "Color(0.5019608, 0.5019608, 0.5019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/WEB_GREEN': value changed value in new API, from "Color(0, 0.501961, 0, 1)" to "Color(0, 0.5019608, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/WEB_MAROON': value changed value in new API, from "Color(0.501961, 0, 0, 1)" to "Color(0.5019608, 0, 0, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/WEB_PURPLE': value changed value in new API, from "Color(0.501961, 0, 0.501961, 1)" to "Color(0.5019608, 0, 0.5019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/WHEAT': value changed value in new API, from "Color(0.960784, 0.870588, 0.701961, 1)" to "Color(0.9607843, 0.87058824, 0.7019608, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/WHITE_SMOKE': value changed value in new API, from "Color(0.960784, 0.960784, 0.960784, 1)" to "Color(0.9607843, 0.9607843, 0.9607843, 1)".
+Validate extension JSON: Error: Field 'builtin_classes/Color/constants/YELLOW_GREEN': value changed value in new API, from "Color(0.603922, 0.803922, 0.196078, 1)" to "Color(0.6039216, 0.8039216, 0.19607843, 1)".
+Validate extension JSON: Error: Field 'classes/InputMap/methods/add_action/arguments/1': default_value changed value in new API, from "0.2" to "0.20000000298023224".
+Validate extension JSON: Error: Field 'classes/InputMap/methods/add_action/arguments/1': default_value changed value in new API, from "0.5" to "0.20000000298023224".
+Validate extension JSON: Error: Field 'global_enums/KeyModifierMask/values/KEY_MODIFIER_MASK': value changed value in new API, from 532676600.0 to 2130706432.
+
+Precision of string-serialized Variant constants increased.

+ 1 - 1
modules/gltf/doc_classes/GLTFCamera.xml

@@ -46,7 +46,7 @@
 		<member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
 		<member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
 			The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]znear[/code] property.
 			The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]znear[/code] property.
 		</member>
 		</member>
-		<member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309">
+		<member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.3089969">
 			The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is [code]true[/code].
 			The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is [code]true[/code].
 		</member>
 		</member>
 		<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
 		<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">

+ 1 - 1
modules/gltf/doc_classes/GLTFLight.xml

@@ -66,7 +66,7 @@
 		<member name="light_type" type="String" setter="set_light_type" getter="get_light_type" default="&quot;&quot;">
 		<member name="light_type" type="String" setter="set_light_type" getter="get_light_type" default="&quot;&quot;">
 			The type of the light. The values accepted by Godot are "point", "spot", and "directional", which correspond to Godot's [OmniLight3D], [SpotLight3D], and [DirectionalLight3D] respectively.
 			The type of the light. The values accepted by Godot are "point", "spot", and "directional", which correspond to Godot's [OmniLight3D], [SpotLight3D], and [DirectionalLight3D] respectively.
 		</member>
 		</member>
-		<member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.785398">
+		<member name="outer_cone_angle" type="float" setter="set_outer_cone_angle" getter="get_outer_cone_angle" default="0.7853982">
 			The outer angle of the cone in a spotlight. Must be greater than or equal to the inner angle.
 			The outer angle of the cone in a spotlight. Must be greater than or equal to the inner angle.
 			At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight.
 			At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight.
 		</member>
 		</member>

+ 1 - 1
modules/openxr/doc_classes/OpenXRCompositionLayerCylinder.xml

@@ -12,7 +12,7 @@
 		<member name="aspect_ratio" type="float" setter="set_aspect_ratio" getter="get_aspect_ratio" default="1.0">
 		<member name="aspect_ratio" type="float" setter="set_aspect_ratio" getter="get_aspect_ratio" default="1.0">
 			The aspect ratio of the slice. Used to set the height relative to the width.
 			The aspect ratio of the slice. Used to set the height relative to the width.
 		</member>
 		</member>
-		<member name="central_angle" type="float" setter="set_central_angle" getter="get_central_angle" default="1.5708">
+		<member name="central_angle" type="float" setter="set_central_angle" getter="get_central_angle" default="1.5707964">
 			The central angle of the cylinder. Used to set the width.
 			The central angle of the cylinder. Used to set the width.
 		</member>
 		</member>
 		<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
 		<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">

+ 3 - 3
modules/openxr/doc_classes/OpenXRCompositionLayerEquirect.xml

@@ -9,19 +9,19 @@
 	<tutorials>
 	<tutorials>
 	</tutorials>
 	</tutorials>
 	<members>
 	<members>
-		<member name="central_horizontal_angle" type="float" setter="set_central_horizontal_angle" getter="get_central_horizontal_angle" default="1.5708">
+		<member name="central_horizontal_angle" type="float" setter="set_central_horizontal_angle" getter="get_central_horizontal_angle" default="1.5707964">
 			The central horizontal angle of the sphere. Used to set the width.
 			The central horizontal angle of the sphere. Used to set the width.
 		</member>
 		</member>
 		<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
 		<member name="fallback_segments" type="int" setter="set_fallback_segments" getter="get_fallback_segments" default="10">
 			The number of segments to use in the fallback mesh.
 			The number of segments to use in the fallback mesh.
 		</member>
 		</member>
-		<member name="lower_vertical_angle" type="float" setter="set_lower_vertical_angle" getter="get_lower_vertical_angle" default="0.785398">
+		<member name="lower_vertical_angle" type="float" setter="set_lower_vertical_angle" getter="get_lower_vertical_angle" default="0.7853982">
 			The lower vertical angle of the sphere. Used (together with [member upper_vertical_angle]) to set the height.
 			The lower vertical angle of the sphere. Used (together with [member upper_vertical_angle]) to set the height.
 		</member>
 		</member>
 		<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
 		<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
 			The radius of the sphere.
 			The radius of the sphere.
 		</member>
 		</member>
-		<member name="upper_vertical_angle" type="float" setter="set_upper_vertical_angle" getter="get_upper_vertical_angle" default="0.785398">
+		<member name="upper_vertical_angle" type="float" setter="set_upper_vertical_angle" getter="get_upper_vertical_angle" default="0.7853982">
 			The upper vertical angle of the sphere. Used (together with [member lower_vertical_angle]) to set the height.
 			The upper vertical angle of the sphere. Used (together with [member lower_vertical_angle]) to set the height.
 		</member>
 		</member>
 	</members>
 	</members>

+ 1 - 1
modules/openxr/doc_classes/OpenXRDpadBindingModifier.xml

@@ -36,7 +36,7 @@
 		<member name="threshold_released" type="float" setter="set_threshold_released" getter="get_threshold_released" default="0.4">
 		<member name="threshold_released" type="float" setter="set_threshold_released" getter="get_threshold_released" default="0.4">
 			When our input value falls below this, our output becomes [code]false[/code].
 			When our input value falls below this, our output becomes [code]false[/code].
 		</member>
 		</member>
-		<member name="wedge_angle" type="float" setter="set_wedge_angle" getter="get_wedge_angle" default="1.5708">
+		<member name="wedge_angle" type="float" setter="set_wedge_angle" getter="get_wedge_angle" default="1.5707964">
 			The angle of each wedge that identifies the 4 directions of the emulated dpad.
 			The angle of each wedge that identifies the 4 directions of the emulated dpad.
 		</member>
 		</member>
 	</members>
 	</members>

+ 14 - 1
tests/core/string/test_string.h

@@ -546,7 +546,6 @@ TEST_CASE("[String] Number to string") {
 	CHECK(String::num(3.141593) == "3.141593");
 	CHECK(String::num(3.141593) == "3.141593");
 	CHECK(String::num(3.141593, 3) == "3.142");
 	CHECK(String::num(3.141593, 3) == "3.142");
 	CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
 	CHECK(String::num(42.100023, 4) == "42.1"); // No trailing zeros.
-	CHECK(String::num_scientific(30000000) == "3e+07");
 
 
 	// String::num_int64 tests.
 	// String::num_int64 tests.
 	CHECK(String::num_int64(3141593) == "3141593");
 	CHECK(String::num_int64(3141593) == "3141593");
@@ -567,6 +566,20 @@ TEST_CASE("[String] Number to string") {
 	CHECK(String::num_uint64(4294967295, 37) == ""); // Invalid base > 36.
 	CHECK(String::num_uint64(4294967295, 37) == ""); // Invalid base > 36.
 	ERR_PRINT_ON;
 	ERR_PRINT_ON;
 
 
+	// String::num_scientific tests.
+	CHECK(String::num_scientific(30000000.0) == "30000000");
+	CHECK(String::num_scientific(1234567890.0) == "1234567890");
+	CHECK(String::num_scientific(3e100) == "3e+100");
+	CHECK(String::num_scientific(7e-100) == "7e-100");
+	CHECK(String::num_scientific(Math::TAU) == "6.283185307179586");
+	CHECK(String::num_scientific(Math::INF) == "inf");
+	CHECK(String::num_scientific(-Math::INF) == "-inf");
+	CHECK(String::num_scientific(Math::NaN) == "nan");
+	CHECK(String::num_scientific(2.0) == "2");
+	CHECK(String::num_scientific(1.0) == "1");
+	CHECK(String::num_scientific(0.0) == "0");
+	CHECK(String::num_scientific(-0.0) == "-0");
+
 	// String::num_real tests.
 	// String::num_real tests.
 	CHECK(String::num_real(1.0) == "1.0");
 	CHECK(String::num_real(1.0) == "1.0");
 	CHECK(String::num_real(1.0, false) == "1");
 	CHECK(String::num_real(1.0, false) == "1");

+ 34 - 2
tests/core/variant/test_variant.h

@@ -101,7 +101,7 @@ TEST_CASE("[Variant] Writer and parser Variant::FLOAT") {
 	String a64_str;
 	String a64_str;
 	VariantWriter::write_to_string(a64, a64_str);
 	VariantWriter::write_to_string(a64, a64_str);
 
 
-	CHECK_MESSAGE(a64_str == "1.79769e+308", "Writes in scientific notation.");
+	CHECK_MESSAGE(a64_str == "1.7976931348623157e+308", "Writes in scientific notation.");
 	CHECK_MESSAGE(a64_str != "inf", "Should not overflow.");
 	CHECK_MESSAGE(a64_str != "inf", "Should not overflow.");
 	CHECK_MESSAGE(a64_str != "nan", "The result should be defined.");
 	CHECK_MESSAGE(a64_str != "nan", "The result should be defined.");
 
 
@@ -115,7 +115,7 @@ TEST_CASE("[Variant] Writer and parser Variant::FLOAT") {
 	VariantParser::parse(&bss, variant_parsed, errs, line);
 	VariantParser::parse(&bss, variant_parsed, errs, line);
 	float_parsed = variant_parsed;
 	float_parsed = variant_parsed;
 	// Loses precision, but that's alright.
 	// Loses precision, but that's alright.
-	CHECK_MESSAGE(float_parsed == 1.79769e+308, "Should parse back.");
+	CHECK_MESSAGE(float_parsed == 1.797693134862315708145274237317e+308, "Should parse back.");
 
 
 	// Approximation of Googol with a double-precision float.
 	// Approximation of Googol with a double-precision float.
 	VariantParser::StreamString css;
 	VariantParser::StreamString css;
@@ -1754,6 +1754,38 @@ TEST_CASE("[Variant] array initializer list") {
 	CHECK(packed_arr[2] == 0);
 	CHECK(packed_arr[2] == 0);
 }
 }
 
 
+TEST_CASE("[Variant] Writer and parser Vector2") {
+	Variant vec2_parsed;
+	String vec2_str;
+	String errs;
+	int line;
+	// Variant::VECTOR2 and Vector2 can be either 32-bit or 64-bit depending on the precision level of real_t.
+	{
+		Vector2 vec2 = Vector2(1.2, 3.4);
+		VariantWriter::write_to_string(vec2, vec2_str);
+		// Reminder: "1.2" and "3.4" are not exactly those decimal numbers. They are the closest float to them.
+		CHECK_MESSAGE(vec2_str == "Vector2(1.2, 3.4)", "Should write with enough digits to ensure parsing back is exact.");
+		VariantParser::StreamString stream;
+		stream.s = vec2_str;
+		VariantParser::parse(&stream, vec2_parsed, errs, line);
+		CHECK_MESSAGE(Vector2(vec2_parsed) == vec2, "Should parse back to the same Vector2.");
+	}
+	// Check with big numbers and small numbers.
+	{
+		Vector2 vec2 = Vector2(1.234567898765432123456789e30, 1.234567898765432123456789e-10);
+		VariantWriter::write_to_string(vec2, vec2_str);
+#ifdef REAL_T_IS_DOUBLE
+		CHECK_MESSAGE(vec2_str == "Vector2(1.2345678987654322e+30, 1.2345678987654322e-10)", "Should write with enough digits to ensure parsing back is exact.");
+#else
+		CHECK_MESSAGE(vec2_str == "Vector2(1.2345679e+30, 1.2345679e-10)", "Should write with enough digits to ensure parsing back is exact.");
+#endif
+		VariantParser::StreamString stream;
+		stream.s = vec2_str;
+		VariantParser::parse(&stream, vec2_parsed, errs, line);
+		CHECK_MESSAGE(Vector2(vec2_parsed) == vec2, "Should parse back to the same Vector2.");
+	}
+}
+
 TEST_CASE("[Variant] Writer and parser array") {
 TEST_CASE("[Variant] Writer and parser array") {
 	Array a = build_array(1, String("hello"), build_array(Variant()));
 	Array a = build_array(1, String("hello"), build_array(Variant()));
 	String a_str;
 	String a_str;

+ 15 - 0
thirdparty/README.md

@@ -415,6 +415,21 @@ Files extracted from upstream source:
 - `COPYING`
 - `COPYING`
 
 
 
 
+## grisu2
+
+- Upstream: https://github.com/simdjson/simdjson/blob/master/src/to_chars.cpp
+- Version: git (4f4e81668ecb9d4d37fd5f59a1556d492507421d, 2023)
+- License: Apache and MIT
+
+Files extracted from upstream source:
+
+- The `src/to_chars.cpp` file renamed to `grisu2.h` and slightly modified.
+
+Patches:
+
+- `0001-godot-changes.patch` (GH-98750)
+
+
 ## harfbuzz
 ## harfbuzz
 
 
 - Upstream: https://github.com/harfbuzz/harfbuzz
 - Upstream: https://github.com/harfbuzz/harfbuzz

+ 47 - 0
thirdparty/grisu2/LICENSE

@@ -0,0 +1,47 @@
+The Grisu2 algorithm is by Florian Loitsch, based on the work of Robert G. Burger and R. Kent Dybvig:
+
+    [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, PLDI 2010
+    [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, PLDI 1996
+
+
+The original C implementation is by Florian Loitsch:
+
+    Copyright (c) 2009 Florian Loitsch
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use,
+    copy, modify, merge, publish, distribute, sublicense, and/or sell
+    copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following
+    conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+    OTHER DEALINGS IN THE SOFTWARE.
+
+
+The implementation simplified and adapted to JSON and C++11 by Daniel Lemire as part of simdjson:
+
+    Copyright 2018-2023 The simdjson authors
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.

+ 20 - 0
thirdparty/grisu2/README.md

@@ -0,0 +1,20 @@
+# Grisu2
+
+This is a C++11 implementation of the Grisu2 algorithm for converting floating-point numbers to decimal strings.
+
+The Grisu2 algorithm is by Florian Loitsch, based on the work of Robert G. Burger and R. Kent Dybvig:
+
+- https://dl.acm.org/doi/10.1145/1806596.1806623 [1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation, PLDI 2010
+- https://dl.acm.org/doi/10.1145/231379.231397 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language Design and Implementation, PLDI 1996
+
+The original C implementation is by Florian Loitsch:
+- https://drive.google.com/file/d/0BwvYOx00EwKmejFIMjRORTFLcTA/view?resourcekey=0-1Lg8tXTC_JAODUcFpMcaTA
+
+The implementation simplified and adapted to JSON and C++11 by Daniel Lemire as part of simdjson:
+- https://github.com/simdjson/simdjson/blob/master/src/to_chars.cpp
+
+The `grisu2.h` file is the same as `to_chars.cpp` but with `godot.patch` applied to it, with the following changes:
+- Simplify namespaces to just be one `grisu2` namespace.
+- Rename functions to ensure their names are unique.
+- Make `to_chars` handle both float and double types instead of just double.
+- Remove the trailing `.0` logic to match Godot's existing `String::num_scientific` behavior.

+ 936 - 0
thirdparty/grisu2/grisu2.h

@@ -0,0 +1,936 @@
+#pragma once
+
+#include <cstring>
+#include <cstdint>
+#include <array>
+#include <cmath>
+
+namespace grisu2 {
+/*!
+implements the Grisu2 algorithm for binary to decimal floating-point
+conversion.
+Adapted from JSON for Modern C++
+
+This implementation is a slightly modified version of the reference
+implementation which may be obtained from
+http://florian.loitsch.com/publications (bench.tar.gz).
+The code is distributed under the MIT license, Copyright (c) 2009 Florian
+Loitsch. For a detailed description of the algorithm see: [1] Loitsch, "Printing
+Floating-Point Numbers Quickly and Accurately with Integers", Proceedings of the
+ACM SIGPLAN 2010 Conference on Programming Language Design and Implementation,
+PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and
+Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming
+Language Design and Implementation, PLDI 1996
+*/
+
+template <typename Target, typename Source>
+Target reinterpret_bits(const Source source) {
+  static_assert(sizeof(Target) == sizeof(Source), "size mismatch");
+
+  Target target;
+  std::memcpy(&target, &source, sizeof(Source));
+  return target;
+}
+
+struct diyfp // f * 2^e
+{
+  static constexpr int kPrecision = 64; // = q
+
+  std::uint64_t f = 0;
+  int e = 0;
+
+  constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {}
+
+  /*!
+  @brief returns x - y
+  @pre x.e == y.e and x.f >= y.f
+  */
+  static diyfp sub(const diyfp &x, const diyfp &y) noexcept {
+
+    return {x.f - y.f, x.e};
+  }
+
+  /*!
+  @brief returns x * y
+  @note The result is rounded. (Only the upper q bits are returned.)
+  */
+  static diyfp mul(const diyfp &x, const diyfp &y) noexcept {
+    static_assert(kPrecision == 64, "internal error");
+
+    // Computes:
+    //  f = round((x.f * y.f) / 2^q)
+    //  e = x.e + y.e + q
+
+    // Emulate the 64-bit * 64-bit multiplication:
+    //
+    // p = u * v
+    //   = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi)
+    //   = (u_lo v_lo         ) + 2^32 ((u_lo v_hi         ) + (u_hi v_lo )) +
+    //   2^64 (u_hi v_hi         ) = (p0                ) + 2^32 ((p1 ) + (p2 ))
+    //   + 2^64 (p3                ) = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo +
+    //   2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3                ) =
+    //   (p0_lo             ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi +
+    //   p2_hi + p3) = (p0_lo             ) + 2^32 (Q ) + 2^64 (H ) = (p0_lo ) +
+    //   2^32 (Q_lo + 2^32 Q_hi                           ) + 2^64 (H )
+    //
+    // (Since Q might be larger than 2^32 - 1)
+    //
+    //   = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H)
+    //
+    // (Q_hi + H does not overflow a 64-bit int)
+    //
+    //   = p_lo + 2^64 p_hi
+
+    const std::uint64_t u_lo = x.f & 0xFFFFFFFFu;
+    const std::uint64_t u_hi = x.f >> 32u;
+    const std::uint64_t v_lo = y.f & 0xFFFFFFFFu;
+    const std::uint64_t v_hi = y.f >> 32u;
+
+    const std::uint64_t p0 = u_lo * v_lo;
+    const std::uint64_t p1 = u_lo * v_hi;
+    const std::uint64_t p2 = u_hi * v_lo;
+    const std::uint64_t p3 = u_hi * v_hi;
+
+    const std::uint64_t p0_hi = p0 >> 32u;
+    const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu;
+    const std::uint64_t p1_hi = p1 >> 32u;
+    const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu;
+    const std::uint64_t p2_hi = p2 >> 32u;
+
+    std::uint64_t Q = p0_hi + p1_lo + p2_lo;
+
+    // The full product might now be computed as
+    //
+    // p_hi = p3 + p2_hi + p1_hi + (Q >> 32)
+    // p_lo = p0_lo + (Q << 32)
+    //
+    // But in this particular case here, the full p_lo is not required.
+    // Effectively we only need to add the highest bit in p_lo to p_hi (and
+    // Q_hi + 1 does not overflow).
+
+    Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up
+
+    const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u);
+
+    return {h, x.e + y.e + 64};
+  }
+
+  /*!
+  @brief normalize x such that the significand is >= 2^(q-1)
+  @pre x.f != 0
+  */
+  static diyfp normalize(diyfp x) noexcept {
+
+    while ((x.f >> 63u) == 0) {
+      x.f <<= 1u;
+      x.e--;
+    }
+
+    return x;
+  }
+
+  /*!
+  @brief normalize x such that the result has the exponent E
+  @pre e >= x.e and the upper e - x.e bits of x.f must be zero.
+  */
+  static diyfp normalize_to(const diyfp &x,
+                            const int target_exponent) noexcept {
+    const int delta = x.e - target_exponent;
+
+    return {x.f << delta, target_exponent};
+  }
+};
+
+struct boundaries {
+  diyfp w;
+  diyfp minus;
+  diyfp plus;
+};
+
+/*!
+Compute the (normalized) diyfp representing the input number 'value' and its
+boundaries.
+@pre value must be finite and positive
+*/
+template <typename FloatType> boundaries compute_boundaries(FloatType value) {
+
+  // Convert the IEEE representation into a diyfp.
+  //
+  // If v is denormal:
+  //      value = 0.F * 2^(1 - bias) = (          F) * 2^(1 - bias - (p-1))
+  // If v is normalized:
+  //      value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1))
+
+  static_assert(std::numeric_limits<FloatType>::is_iec559,
+                "internal error: dtoa_short requires an IEEE-754 "
+                "floating-point implementation");
+
+  constexpr int kPrecision =
+      std::numeric_limits<FloatType>::digits; // = p (includes the hidden bit)
+  constexpr int kBias =
+      std::numeric_limits<FloatType>::max_exponent - 1 + (kPrecision - 1);
+  constexpr int kMinExp = 1 - kBias;
+  constexpr std::uint64_t kHiddenBit = std::uint64_t{1}
+                                       << (kPrecision - 1); // = 2^(p-1)
+
+  using bits_type = typename std::conditional<kPrecision == 24, std::uint32_t,
+                                              std::uint64_t>::type;
+
+  const std::uint64_t bits = reinterpret_bits<bits_type>(value);
+  const std::uint64_t E = bits >> (kPrecision - 1);
+  const std::uint64_t F = bits & (kHiddenBit - 1);
+
+  const bool is_denormal = E == 0;
+  const diyfp v = is_denormal
+                      ? diyfp(F, kMinExp)
+                      : diyfp(F + kHiddenBit, static_cast<int>(E) - kBias);
+
+  // Compute the boundaries m- and m+ of the floating-point value
+  // v = f * 2^e.
+  //
+  // Determine v- and v+, the floating-point predecessor and successor if v,
+  // respectively.
+  //
+  //      v- = v - 2^e        if f != 2^(p-1) or e == e_min                (A)
+  //         = v - 2^(e-1)    if f == 2^(p-1) and e > e_min                (B)
+  //
+  //      v+ = v + 2^e
+  //
+  // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_
+  // between m- and m+ round to v, regardless of how the input rounding
+  // algorithm breaks ties.
+  //
+  //      ---+-------------+-------------+-------------+-------------+---  (A)
+  //         v-            m-            v             m+            v+
+  //
+  //      -----------------+------+------+-------------+-------------+---  (B)
+  //                       v-     m-     v             m+            v+
+
+  const bool lower_boundary_is_closer = F == 0 && E > 1;
+  const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1);
+  const diyfp m_minus = lower_boundary_is_closer
+                            ? diyfp(4 * v.f - 1, v.e - 2)  // (B)
+                            : diyfp(2 * v.f - 1, v.e - 1); // (A)
+
+  // Determine the normalized w+ = m+.
+  const diyfp w_plus = diyfp::normalize(m_plus);
+
+  // Determine w- = m- such that e_(w-) = e_(w+).
+  const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e);
+
+  return {diyfp::normalize(v), w_minus, w_plus};
+}
+
+// Given normalized diyfp w, Grisu needs to find a (normalized) cached
+// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies
+// within a certain range [alpha, gamma] (Definition 3.2 from [1])
+//
+//      alpha <= e = e_c + e_w + q <= gamma
+//
+// or
+//
+//      f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q
+//                          <= f_c * f_w * 2^gamma
+//
+// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies
+//
+//      2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma
+//
+// or
+//
+//      2^(q - 2 + alpha) <= c * w < 2^(q + gamma)
+//
+// The choice of (alpha,gamma) determines the size of the table and the form of
+// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well
+// in practice:
+//
+// The idea is to cut the number c * w = f * 2^e into two parts, which can be
+// processed independently: An integral part p1, and a fractional part p2:
+//
+//      f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e
+//              = (f div 2^-e) + (f mod 2^-e) * 2^e
+//              = p1 + p2 * 2^e
+//
+// The conversion of p1 into decimal form requires a series of divisions and
+// modulos by (a power of) 10. These operations are faster for 32-bit than for
+// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be
+// achieved by choosing
+//
+//      -e >= 32   or   e <= -32 := gamma
+//
+// In order to convert the fractional part
+//
+//      p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ...
+//
+// into decimal form, the fraction is repeatedly multiplied by 10 and the digits
+// d[-i] are extracted in order:
+//
+//      (10 * p2) div 2^-e = d[-1]
+//      (10 * p2) mod 2^-e = d[-2] / 10^1 + ...
+//
+// The multiplication by 10 must not overflow. It is sufficient to choose
+//
+//      10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64.
+//
+// Since p2 = f mod 2^-e < 2^-e,
+//
+//      -e <= 60   or   e >= -60 := alpha
+
+constexpr int kAlpha = -60;
+constexpr int kGamma = -32;
+
+struct cached_power // c = f * 2^e ~= 10^k
+{
+  std::uint64_t f;
+  int e;
+  int k;
+};
+
+/*!
+For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached
+power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c
+satisfies (Definition 3.2 from [1])
+     alpha <= e_c + e + q <= gamma.
+*/
+inline cached_power get_cached_power_for_binary_exponent(int e) {
+  // Now
+  //
+  //      alpha <= e_c + e + q <= gamma                                    (1)
+  //      ==> f_c * 2^alpha <= c * 2^e * 2^q
+  //
+  // and since the c's are normalized, 2^(q-1) <= f_c,
+  //
+  //      ==> 2^(q - 1 + alpha) <= c * 2^(e + q)
+  //      ==> 2^(alpha - e - 1) <= c
+  //
+  // If c were an exact power of ten, i.e. c = 10^k, one may determine k as
+  //
+  //      k = ceil( log_10( 2^(alpha - e - 1) ) )
+  //        = ceil( (alpha - e - 1) * log_10(2) )
+  //
+  // From the paper:
+  // "In theory the result of the procedure could be wrong since c is rounded,
+  //  and the computation itself is approximated [...]. In practice, however,
+  //  this simple function is sufficient."
+  //
+  // For IEEE double precision floating-point numbers converted into
+  // normalized diyfp's w = f * 2^e, with q = 64,
+  //
+  //      e >= -1022      (min IEEE exponent)
+  //           -52        (p - 1)
+  //           -52        (p - 1, possibly normalize denormal IEEE numbers)
+  //           -11        (normalize the diyfp)
+  //         = -1137
+  //
+  // and
+  //
+  //      e <= +1023      (max IEEE exponent)
+  //           -52        (p - 1)
+  //           -11        (normalize the diyfp)
+  //         = 960
+  //
+  // This binary exponent range [-1137,960] results in a decimal exponent
+  // range [-307,324]. One does not need to store a cached power for each
+  // k in this range. For each such k it suffices to find a cached power
+  // such that the exponent of the product lies in [alpha,gamma].
+  // This implies that the difference of the decimal exponents of adjacent
+  // table entries must be less than or equal to
+  //
+  //      floor( (gamma - alpha) * log_10(2) ) = 8.
+  //
+  // (A smaller distance gamma-alpha would require a larger table.)
+
+  // NB:
+  // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34.
+
+  constexpr int kCachedPowersMinDecExp = -300;
+  constexpr int kCachedPowersDecStep = 8;
+
+  static constexpr std::array<cached_power, 79> kCachedPowers = {{
+      {0xAB70FE17C79AC6CA, -1060, -300}, {0xFF77B1FCBEBCDC4F, -1034, -292},
+      {0xBE5691EF416BD60C, -1007, -284}, {0x8DD01FAD907FFC3C, -980, -276},
+      {0xD3515C2831559A83, -954, -268},  {0x9D71AC8FADA6C9B5, -927, -260},
+      {0xEA9C227723EE8BCB, -901, -252},  {0xAECC49914078536D, -874, -244},
+      {0x823C12795DB6CE57, -847, -236},  {0xC21094364DFB5637, -821, -228},
+      {0x9096EA6F3848984F, -794, -220},  {0xD77485CB25823AC7, -768, -212},
+      {0xA086CFCD97BF97F4, -741, -204},  {0xEF340A98172AACE5, -715, -196},
+      {0xB23867FB2A35B28E, -688, -188},  {0x84C8D4DFD2C63F3B, -661, -180},
+      {0xC5DD44271AD3CDBA, -635, -172},  {0x936B9FCEBB25C996, -608, -164},
+      {0xDBAC6C247D62A584, -582, -156},  {0xA3AB66580D5FDAF6, -555, -148},
+      {0xF3E2F893DEC3F126, -529, -140},  {0xB5B5ADA8AAFF80B8, -502, -132},
+      {0x87625F056C7C4A8B, -475, -124},  {0xC9BCFF6034C13053, -449, -116},
+      {0x964E858C91BA2655, -422, -108},  {0xDFF9772470297EBD, -396, -100},
+      {0xA6DFBD9FB8E5B88F, -369, -92},   {0xF8A95FCF88747D94, -343, -84},
+      {0xB94470938FA89BCF, -316, -76},   {0x8A08F0F8BF0F156B, -289, -68},
+      {0xCDB02555653131B6, -263, -60},   {0x993FE2C6D07B7FAC, -236, -52},
+      {0xE45C10C42A2B3B06, -210, -44},   {0xAA242499697392D3, -183, -36},
+      {0xFD87B5F28300CA0E, -157, -28},   {0xBCE5086492111AEB, -130, -20},
+      {0x8CBCCC096F5088CC, -103, -12},   {0xD1B71758E219652C, -77, -4},
+      {0x9C40000000000000, -50, 4},      {0xE8D4A51000000000, -24, 12},
+      {0xAD78EBC5AC620000, 3, 20},       {0x813F3978F8940984, 30, 28},
+      {0xC097CE7BC90715B3, 56, 36},      {0x8F7E32CE7BEA5C70, 83, 44},
+      {0xD5D238A4ABE98068, 109, 52},     {0x9F4F2726179A2245, 136, 60},
+      {0xED63A231D4C4FB27, 162, 68},     {0xB0DE65388CC8ADA8, 189, 76},
+      {0x83C7088E1AAB65DB, 216, 84},     {0xC45D1DF942711D9A, 242, 92},
+      {0x924D692CA61BE758, 269, 100},    {0xDA01EE641A708DEA, 295, 108},
+      {0xA26DA3999AEF774A, 322, 116},    {0xF209787BB47D6B85, 348, 124},
+      {0xB454E4A179DD1877, 375, 132},    {0x865B86925B9BC5C2, 402, 140},
+      {0xC83553C5C8965D3D, 428, 148},    {0x952AB45CFA97A0B3, 455, 156},
+      {0xDE469FBD99A05FE3, 481, 164},    {0xA59BC234DB398C25, 508, 172},
+      {0xF6C69A72A3989F5C, 534, 180},    {0xB7DCBF5354E9BECE, 561, 188},
+      {0x88FCF317F22241E2, 588, 196},    {0xCC20CE9BD35C78A5, 614, 204},
+      {0x98165AF37B2153DF, 641, 212},    {0xE2A0B5DC971F303A, 667, 220},
+      {0xA8D9D1535CE3B396, 694, 228},    {0xFB9B7CD9A4A7443C, 720, 236},
+      {0xBB764C4CA7A44410, 747, 244},    {0x8BAB8EEFB6409C1A, 774, 252},
+      {0xD01FEF10A657842C, 800, 260},    {0x9B10A4E5E9913129, 827, 268},
+      {0xE7109BFBA19C0C9D, 853, 276},    {0xAC2820D9623BF429, 880, 284},
+      {0x80444B5E7AA7CF85, 907, 292},    {0xBF21E44003ACDD2D, 933, 300},
+      {0x8E679C2F5E44FF8F, 960, 308},    {0xD433179D9C8CB841, 986, 316},
+      {0x9E19DB92B4E31BA9, 1013, 324},
+  }};
+
+  // This computation gives exactly the same results for k as
+  //      k = ceil((kAlpha - e - 1) * 0.30102999566398114)
+  // for |e| <= 1500, but doesn't require floating-point operations.
+  // NB: log_10(2) ~= 78913 / 2^18
+  const int f = kAlpha - e - 1;
+  const int k = (f * 78913) / (1 << 18) + static_cast<int>(f > 0);
+
+  const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) /
+                    kCachedPowersDecStep;
+
+  const cached_power cached = kCachedPowers[static_cast<std::size_t>(index)];
+
+  return cached;
+}
+
+/*!
+For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k.
+For n == 0, returns 1 and sets pow10 := 1.
+*/
+inline int find_largest_pow10(const std::uint32_t n, std::uint32_t &pow10) {
+  // LCOV_EXCL_START
+  if (n >= 1000000000) {
+    pow10 = 1000000000;
+    return 10;
+  }
+  // LCOV_EXCL_STOP
+  else if (n >= 100000000) {
+    pow10 = 100000000;
+    return 9;
+  } else if (n >= 10000000) {
+    pow10 = 10000000;
+    return 8;
+  } else if (n >= 1000000) {
+    pow10 = 1000000;
+    return 7;
+  } else if (n >= 100000) {
+    pow10 = 100000;
+    return 6;
+  } else if (n >= 10000) {
+    pow10 = 10000;
+    return 5;
+  } else if (n >= 1000) {
+    pow10 = 1000;
+    return 4;
+  } else if (n >= 100) {
+    pow10 = 100;
+    return 3;
+  } else if (n >= 10) {
+    pow10 = 10;
+    return 2;
+  } else {
+    pow10 = 1;
+    return 1;
+  }
+}
+
+inline void grisu2_round(char *buf, int len, std::uint64_t dist,
+                         std::uint64_t delta, std::uint64_t rest,
+                         std::uint64_t ten_k) {
+
+  //               <--------------------------- delta ---->
+  //                                  <---- dist --------->
+  // --------------[------------------+-------------------]--------------
+  //               M-                 w                   M+
+  //
+  //                                  ten_k
+  //                                <------>
+  //                                       <---- rest ---->
+  // --------------[------------------+----+--------------]--------------
+  //                                  w    V
+  //                                       = buf * 10^k
+  //
+  // ten_k represents a unit-in-the-last-place in the decimal representation
+  // stored in buf.
+  // Decrement buf by ten_k while this takes buf closer to w.
+
+  // The tests are written in this order to avoid overflow in unsigned
+  // integer arithmetic.
+
+  while (rest < dist && delta - rest >= ten_k &&
+         (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) {
+    buf[len - 1]--;
+    rest += ten_k;
+  }
+}
+
+/*!
+Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+.
+M- and M+ must be normalized and share the same exponent -60 <= e <= -32.
+*/
+inline void grisu2_digit_gen(char *buffer, int &length, int &decimal_exponent,
+                             diyfp M_minus, diyfp w, diyfp M_plus) {
+  static_assert(kAlpha >= -60, "internal error");
+  static_assert(kGamma <= -32, "internal error");
+
+  // Generates the digits (and the exponent) of a decimal floating-point
+  // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's
+  // w, M- and M+ share the same exponent e, which satisfies alpha <= e <=
+  // gamma.
+  //
+  //               <--------------------------- delta ---->
+  //                                  <---- dist --------->
+  // --------------[------------------+-------------------]--------------
+  //               M-                 w                   M+
+  //
+  // Grisu2 generates the digits of M+ from left to right and stops as soon as
+  // V is in [M-,M+].
+
+  std::uint64_t delta =
+      diyfp::sub(M_plus, M_minus)
+          .f; // (significand of (M+ - M-), implicit exponent is e)
+  std::uint64_t dist =
+      diyfp::sub(M_plus, w)
+          .f; // (significand of (M+ - w ), implicit exponent is e)
+
+  // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0):
+  //
+  //      M+ = f * 2^e
+  //         = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e
+  //         = ((p1        ) * 2^-e + (p2        )) * 2^e
+  //         = p1 + p2 * 2^e
+
+  const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e);
+
+  auto p1 = static_cast<std::uint32_t>(
+      M_plus.f >>
+      -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.)
+  std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e
+
+  // 1)
+  //
+  // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0]
+
+  std::uint32_t pow10;
+  const int k = find_largest_pow10(p1, pow10);
+
+  //      10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1)
+  //
+  //      p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1))
+  //         = (d[k-1]         ) * 10^(k-1) + (p1 mod 10^(k-1))
+  //
+  //      M+ = p1                                             + p2 * 2^e
+  //         = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1))          + p2 * 2^e
+  //         = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e
+  //         = d[k-1] * 10^(k-1) + (                         rest) * 2^e
+  //
+  // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0)
+  //
+  //      p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0]
+  //
+  // but stop as soon as
+  //
+  //      rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e
+
+  int n = k;
+  while (n > 0) {
+    // Invariants:
+    //      M+ = buffer * 10^n + (p1 + p2 * 2^e)    (buffer = 0 for n = k)
+    //      pow10 = 10^(n-1) <= p1 < 10^n
+    //
+    const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1)
+    const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1)
+    //
+    //      M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e
+    //         = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e)
+    //
+    buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+    //
+    //      M+ = buffer * 10^(n-1) + (r + p2 * 2^e)
+    //
+    p1 = r;
+    n--;
+    //
+    //      M+ = buffer * 10^n + (p1 + p2 * 2^e)
+    //      pow10 = 10^n
+    //
+
+    // Now check if enough digits have been generated.
+    // Compute
+    //
+    //      p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e
+    //
+    // Note:
+    // Since rest and delta share the same exponent e, it suffices to
+    // compare the significands.
+    const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2;
+    if (rest <= delta) {
+      // V = buffer * 10^n, with M- <= V <= M+.
+
+      decimal_exponent += n;
+
+      // We may now just stop. But instead look if the buffer could be
+      // decremented to bring V closer to w.
+      //
+      // pow10 = 10^n is now 1 ulp in the decimal representation V.
+      // The rounding procedure works with diyfp's with an implicit
+      // exponent of e.
+      //
+      //      10^n = (10^n * 2^-e) * 2^e = ulp * 2^e
+      //
+      const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e;
+      grisu2_round(buffer, length, dist, delta, rest, ten_n);
+
+      return;
+    }
+
+    pow10 /= 10;
+    //
+    //      pow10 = 10^(n-1) <= p1 < 10^n
+    // Invariants restored.
+  }
+
+  // 2)
+  //
+  // The digits of the integral part have been generated:
+  //
+  //      M+ = d[k-1]...d[1]d[0] + p2 * 2^e
+  //         = buffer            + p2 * 2^e
+  //
+  // Now generate the digits of the fractional part p2 * 2^e.
+  //
+  // Note:
+  // No decimal point is generated: the exponent is adjusted instead.
+  //
+  // p2 actually represents the fraction
+  //
+  //      p2 * 2^e
+  //          = p2 / 2^-e
+  //          = d[-1] / 10^1 + d[-2] / 10^2 + ...
+  //
+  // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...)
+  //
+  //      p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m
+  //                      + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...)
+  //
+  // using
+  //
+  //      10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e)
+  //                = (                   d) * 2^-e + (                   r)
+  //
+  // or
+  //      10^m * p2 * 2^e = d + r * 2^e
+  //
+  // i.e.
+  //
+  //      M+ = buffer + p2 * 2^e
+  //         = buffer + 10^-m * (d + r * 2^e)
+  //         = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e
+  //
+  // and stop as soon as 10^-m * r * 2^e <= delta * 2^e
+
+  int m = 0;
+  for (;;) {
+    // Invariant:
+    //      M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...)
+    //      * 2^e
+    //         = buffer * 10^-m + 10^-m * (p2                                 )
+    //         * 2^e = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e =
+    //         buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e +
+    //         (10*p2 mod 2^-e)) * 2^e
+    //
+    p2 *= 10;
+    const std::uint64_t d = p2 >> -one.e;     // d = (10 * p2) div 2^-e
+    const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e
+    //
+    //      M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e
+    //         = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e))
+    //         = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e
+    //
+    buffer[length++] = static_cast<char>('0' + d); // buffer := buffer * 10 + d
+    //
+    //      M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e
+    //
+    p2 = r;
+    m++;
+    //
+    //      M+ = buffer * 10^-m + 10^-m * p2 * 2^e
+    // Invariant restored.
+
+    // Check if enough digits have been generated.
+    //
+    //      10^-m * p2 * 2^e <= delta * 2^e
+    //              p2 * 2^e <= 10^m * delta * 2^e
+    //                    p2 <= 10^m * delta
+    delta *= 10;
+    dist *= 10;
+    if (p2 <= delta) {
+      break;
+    }
+  }
+
+  // V = buffer * 10^-m, with M- <= V <= M+.
+
+  decimal_exponent -= m;
+
+  // 1 ulp in the decimal representation is now 10^-m.
+  // Since delta and dist are now scaled by 10^m, we need to do the
+  // same with ulp in order to keep the units in sync.
+  //
+  //      10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e
+  //
+  const std::uint64_t ten_m = one.f;
+  grisu2_round(buffer, length, dist, delta, p2, ten_m);
+
+  // By construction this algorithm generates the shortest possible decimal
+  // number (Loitsch, Theorem 6.2) which rounds back to w.
+  // For an input number of precision p, at least
+  //
+  //      N = 1 + ceil(p * log_10(2))
+  //
+  // decimal digits are sufficient to identify all binary floating-point
+  // numbers (Matula, "In-and-Out conversions").
+  // This implies that the algorithm does not produce more than N decimal
+  // digits.
+  //
+  //      N = 17 for p = 53 (IEEE double precision)
+  //      N = 9  for p = 24 (IEEE single precision)
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+inline void grisu2_core(char *buf, int &len, int &decimal_exponent, diyfp m_minus,
+                   diyfp v, diyfp m_plus) {
+
+  //  --------(-----------------------+-----------------------)--------    (A)
+  //          m-                      v                       m+
+  //
+  //  --------------------(-----------+-----------------------)--------    (B)
+  //                      m-          v                       m+
+  //
+  // First scale v (and m- and m+) such that the exponent is in the range
+  // [alpha, gamma].
+
+  const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e);
+
+  const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k
+
+  // The exponent of the products is = v.e + c_minus_k.e + q and is in the range
+  // [alpha,gamma]
+  const diyfp w = diyfp::mul(v, c_minus_k);
+  const diyfp w_minus = diyfp::mul(m_minus, c_minus_k);
+  const diyfp w_plus = diyfp::mul(m_plus, c_minus_k);
+
+  //  ----(---+---)---------------(---+---)---------------(---+---)----
+  //          w-                      w                       w+
+  //          = c*m-                  = c*v                   = c*m+
+  //
+  // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and
+  // w+ are now off by a small amount.
+  // In fact:
+  //
+  //      w - v * 10^k < 1 ulp
+  //
+  // To account for this inaccuracy, add resp. subtract 1 ulp.
+  //
+  //  --------+---[---------------(---+---)---------------]---+--------
+  //          w-  M-                  w                   M+  w+
+  //
+  // Now any number in [M-, M+] (bounds included) will round to w when input,
+  // regardless of how the input rounding algorithm breaks ties.
+  //
+  // And digit_gen generates the shortest possible such number in [M-, M+].
+  // Note that this does not mean that Grisu2 always generates the shortest
+  // possible number in the interval (m-, m+).
+  const diyfp M_minus(w_minus.f + 1, w_minus.e);
+  const diyfp M_plus(w_plus.f - 1, w_plus.e);
+
+  decimal_exponent = -cached.k; // = -(-k) = k
+
+  grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus);
+}
+
+/*!
+v = buf * 10^decimal_exponent
+len is the length of the buffer (number of decimal digits)
+The buffer must be large enough, i.e. >= max_digits10.
+*/
+template <typename FloatType>
+void grisu2_wrap(char *buf, int &len, int &decimal_exponent, FloatType value) {
+  static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+                "internal error: not enough precision");
+
+  // If the neighbors (and boundaries) of 'value' are always computed for
+  // double-precision numbers, all float's can be recovered using strtod (and
+  // strtof). However, the resulting decimal representations are not exactly
+  // "short".
+  //
+  // The documentation for 'std::to_chars'
+  // (https://en.cppreference.com/w/cpp/utility/to_chars) says "value is
+  // converted to a string as if by std::sprintf in the default ("C") locale"
+  // and since sprintf promotes float's to double's, I think this is exactly
+  // what 'std::to_chars' does. On the other hand, the documentation for
+  // 'std::to_chars' requires that "parsing the representation using the
+  // corresponding std::from_chars function recovers value exactly". That
+  // indicates that single precision floating-point numbers should be recovered
+  // using 'std::strtof'.
+  //
+  // NB: If the neighbors are computed for single-precision numbers, there is a
+  // single float
+  //     (7.0385307e-26f) which can't be recovered using strtod. The resulting
+  //     double precision value is off by 1 ulp.
+#if 0
+    const boundaries w = compute_boundaries(static_cast<double>(value));
+#else
+  const boundaries w = compute_boundaries(value);
+#endif
+
+  grisu2_core(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+}
+
+/*!
+@brief appends a decimal representation of e to buf
+@return a pointer to the element following the exponent.
+@pre -1000 < e < 1000
+*/
+inline char *append_exponent(char *buf, int e) {
+
+  if (e < 0) {
+    e = -e;
+    *buf++ = '-';
+  } else {
+    *buf++ = '+';
+  }
+
+  auto k = static_cast<std::uint32_t>(e);
+  if (k < 10) {
+    // Always print at least two digits in the exponent.
+    // This is for compatibility with printf("%g").
+    *buf++ = '0';
+    *buf++ = static_cast<char>('0' + k);
+  } else if (k < 100) {
+    *buf++ = static_cast<char>('0' + k / 10);
+    k %= 10;
+    *buf++ = static_cast<char>('0' + k);
+  } else {
+    *buf++ = static_cast<char>('0' + k / 100);
+    k %= 100;
+    *buf++ = static_cast<char>('0' + k / 10);
+    k %= 10;
+    *buf++ = static_cast<char>('0' + k);
+  }
+
+  return buf;
+}
+
+/*!
+@brief prettify v = buf * 10^decimal_exponent
+If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point
+notation. Otherwise it will be printed in exponential notation.
+@pre min_exp < 0
+@pre max_exp > 0
+*/
+inline char *format_buffer(char *buf, int len, int decimal_exponent,
+                           int min_exp, int max_exp) {
+
+  const int k = len;
+  const int n = len + decimal_exponent;
+
+  // v = buf * 10^(n-k)
+  // k is the length of the buffer (number of decimal digits)
+  // n is the position of the decimal point relative to the start of the buffer.
+
+  if (k <= n && n <= max_exp) {
+    // digits[000]
+    // len <= max_exp + 2
+
+    std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+    return buf + (static_cast<size_t>(n));
+  }
+
+  if (0 < n && n <= max_exp) {
+    // dig.its
+    // len <= max_digits10 + 1
+    std::memmove(buf + (static_cast<size_t>(n) + 1), buf + n,
+                 static_cast<size_t>(k) - static_cast<size_t>(n));
+    buf[n] = '.';
+    return buf + (static_cast<size_t>(k) + 1U);
+  }
+
+  if (min_exp < n && n <= 0) {
+    // 0.[000]digits
+    // len <= 2 + (-min_exp - 1) + max_digits10
+
+    std::memmove(buf + (2 + static_cast<size_t>(-n)), buf,
+                 static_cast<size_t>(k));
+    buf[0] = '0';
+    buf[1] = '.';
+    std::memset(buf + 2, '0', static_cast<size_t>(-n));
+    return buf + (2U + static_cast<size_t>(-n) + static_cast<size_t>(k));
+  }
+
+  if (k == 1) {
+    // dE+123
+    // len <= 1 + 5
+
+    buf += 1;
+  } else {
+    // d.igitsE+123
+    // len <= max_digits10 + 1 + 5
+
+    std::memmove(buf + 2, buf + 1, static_cast<size_t>(k) - 1);
+    buf[1] = '.';
+    buf += 1 + static_cast<size_t>(k);
+  }
+
+  *buf++ = 'e';
+  return append_exponent(buf, n - 1);
+}
+
+/*!
+The format of the resulting decimal representation is similar to printf's %g
+format. Returns an iterator pointing past-the-end of the decimal representation.
+@note The input number must be finite, i.e. NaN's and Inf's are not supported.
+@note The buffer must be large enough.
+@note The result is NOT null-terminated.
+*/
+template <typename FloatType>
+char *to_chars(char *first, FloatType value) {
+  bool negative = std::signbit(value);
+  if (negative) {
+    value = -value;
+    *first++ = '-';
+  }
+  if (value == 0) // +-0
+  {
+    *first++ = '0';
+    return first;
+  }
+  // Compute v = buffer * 10^decimal_exponent.
+  // The decimal digits are stored in the buffer, which needs to be interpreted
+  // as an unsigned decimal integer.
+  // len is the length of the buffer, i.e. the number of decimal digits.
+  int len = 0;
+  int decimal_exponent = 0;
+  grisu2_wrap(first, len, decimal_exponent, value);
+  // Format the buffer like printf("%.*g", prec, value)
+  constexpr int kMinExp = -4;
+  constexpr int kMaxExp = std::numeric_limits<double>::digits10;
+
+  return format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+}
+} // namespace grisu2

+ 119 - 0
thirdparty/grisu2/patches/0001-godot-changes.patch

@@ -0,0 +1,119 @@
+diff --git a/thirdparty/grisu2/grisu2.h b/thirdparty/grisu2/grisu2.h
+index 19886cce47f..dbc09755fad 100644
+--- a/thirdparty/grisu2/grisu2.h
++++ b/thirdparty/grisu2/grisu2.h
+@@ -1,15 +1,12 @@
+-#ifndef SIMDJSON_SRC_TO_CHARS_CPP
+-#define SIMDJSON_SRC_TO_CHARS_CPP
+-
+-#include <base.h>
++#pragma once
+ 
+ #include <cstring>
+ #include <cstdint>
+ #include <array>
+ #include <cmath>
+ 
+-namespace simdjson {
+-namespace internal {
++namespace grisu2 {
+ /*!
+ implements the Grisu2 algorithm for binary to decimal floating-point
+ conversion.
+@@ -26,7 +23,6 @@ PLDI 2010 [2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and
+ Accurately", Proceedings of the ACM SIGPLAN 1996 Conference on Programming
+ Language Design and Implementation, PLDI 1996
+ */
+-namespace dtoa_impl {
+ 
+ template <typename Target, typename Source>
+ Target reinterpret_bits(const Source source) {
+@@ -718,7 +714,7 @@ v = buf * 10^decimal_exponent
+ len is the length of the buffer (number of decimal digits)
+ The buffer must be large enough, i.e. >= max_digits10.
+ */
+-inline void grisu2(char *buf, int &len, int &decimal_exponent, diyfp m_minus,
++inline void grisu2_core(char *buf, int &len, int &decimal_exponent, diyfp m_minus,
+                    diyfp v, diyfp m_plus) {
+ 
+   //  --------(-----------------------+-----------------------)--------    (A)
+@@ -775,7 +771,7 @@ len is the length of the buffer (number of decimal digits)
+ The buffer must be large enough, i.e. >= max_digits10.
+ */
+ template <typename FloatType>
+-void grisu2(char *buf, int &len, int &decimal_exponent, FloatType value) {
++void grisu2_wrap(char *buf, int &len, int &decimal_exponent, FloatType value) {
+   static_assert(diyfp::kPrecision >= std::numeric_limits<FloatType>::digits + 3,
+                 "internal error: not enough precision");
+ 
+@@ -804,7 +800,7 @@ void grisu2(char *buf, int &len, int &decimal_exponent, FloatType value) {
+   const boundaries w = compute_boundaries(value);
+ #endif
+ 
+-  grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus);
++  grisu2_core(buf, len, decimal_exponent, w.minus, w.w, w.plus);
+ }
+ 
+ /*!
+@@ -864,10 +860,7 @@ inline char *format_buffer(char *buf, int len, int decimal_exponent,
+     // len <= max_exp + 2
+ 
+     std::memset(buf + k, '0', static_cast<size_t>(n) - static_cast<size_t>(k));
+-    // Make it look like a floating-point number (#362, #378)
+-    buf[n + 0] = '.';
+-    buf[n + 1] = '0';
+-    return buf + (static_cast<size_t>(n)) + 2;
++    return buf + (static_cast<size_t>(n));
+   }
+ 
+   if (0 < n && n <= max_exp) {
+@@ -909,8 +902,6 @@ inline char *format_buffer(char *buf, int len, int decimal_exponent,
+   return append_exponent(buf, n - 1);
+ }
+ 
+-} // namespace dtoa_impl
+-
+ /*!
+ The format of the resulting decimal representation is similar to printf's %g
+ format. Returns an iterator pointing past-the-end of the decimal representation.
+@@ -918,19 +909,15 @@ format. Returns an iterator pointing past-the-end of the decimal representation.
+ @note The buffer must be large enough.
+ @note The result is NOT null-terminated.
+ */
+-char *to_chars(char *first, const char *last, double value) {
+-  static_cast<void>(last); // maybe unused - fix warning
++template <typename FloatType>
++char *to_chars(char *first, FloatType value) {
+   bool negative = std::signbit(value);
+   if (negative) {
+     value = -value;
+     *first++ = '-';
+   }
+-
+   if (value == 0) // +-0
+   {
+-    *first++ = '0';
+-    // Make it look like a floating-point number (#362, #378)
+-    *first++ = '.';
+     *first++ = '0';
+     return first;
+   }
+@@ -940,15 +927,13 @@ char *to_chars(char *first, const char *last, double value) {
+   // len is the length of the buffer, i.e. the number of decimal digits.
+   int len = 0;
+   int decimal_exponent = 0;
+-  dtoa_impl::grisu2(first, len, decimal_exponent, value);
++  grisu2_wrap(first, len, decimal_exponent, value);
+   // Format the buffer like printf("%.*g", prec, value)
+   constexpr int kMinExp = -4;
+   constexpr int kMaxExp = std::numeric_limits<double>::digits10;
+ 
+-  return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp,
+-                                  kMaxExp);
++  return format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp);
+ }
+-} // namespace internal
+-} // namespace simdjson
++} // namespace grisu2
+ 
+-#endif // SIMDJSON_SRC_TO_CHARS_CPP