From 0c39b0f5ad26ae55649a3da12f55938d9adcee2b Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 22 Sep 2022 15:44:18 -0400
Subject: [PATCH v2 5/9] Don't lose precision for float fields of Nodes.

Historically we've been more worried about making the output of
float fields look pretty than whether they'd be read back exactly.
That won't work if we're to compare the read-back nodes for
equality, so switch to using the Ryu code for float output.

diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index b707a09f56..81b8c184a9 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -983,29 +983,29 @@ _read${n}(void)
 		}
 		elsif ($t eq 'double')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f, \"%.6f\");\n";
+			print $off "\tWRITE_FLOAT_FIELD($f);\n";
 			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
 		}
 		elsif ($t eq 'Cardinality')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f, \"%.0f\");\n";
+			print $off "\tWRITE_FLOAT_FIELD($f);\n";
 			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
 		}
 		elsif ($t eq 'Cost')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f, \"%.2f\");\n";
+			print $off "\tWRITE_FLOAT_FIELD($f);\n";
 			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
 		}
 		elsif ($t eq 'QualCost')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f.startup, \"%.2f\");\n";
-			print $off "\tWRITE_FLOAT_FIELD($f.per_tuple, \"%.2f\");\n";
+			print $off "\tWRITE_FLOAT_FIELD($f.startup);\n";
+			print $off "\tWRITE_FLOAT_FIELD($f.per_tuple);\n";
 			print $rff "\tREAD_FLOAT_FIELD($f.startup);\n"   unless $no_read;
 			print $rff "\tREAD_FLOAT_FIELD($f.per_tuple);\n" unless $no_read;
 		}
 		elsif ($t eq 'Selectivity')
 		{
-			print $off "\tWRITE_FLOAT_FIELD($f, \"%.4f\");\n";
+			print $off "\tWRITE_FLOAT_FIELD($f);\n";
 			print $rff "\tREAD_FLOAT_FIELD($f);\n" unless $no_read;
 		}
 		elsif ($t eq 'char*')
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 63dda75ae5..64c65f060b 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include "access/attnum.h"
+#include "common/shortest_dec.h"
 #include "lib/stringinfo.h"
 #include "miscadmin.h"
 #include "nodes/bitmapset.h"
@@ -25,6 +26,7 @@
 #include "utils/datum.h"
 
 static void outChar(StringInfo str, char c);
+static void outDouble(StringInfo str, double d);
 
 
 /*
@@ -69,9 +71,10 @@ static void outChar(StringInfo str, char c);
 	appendStringInfo(str, " :" CppAsString(fldname) " %d", \
 					 (int) node->fldname)
 
-/* Write a float field --- caller must give format to define precision */
-#define WRITE_FLOAT_FIELD(fldname,format) \
-	appendStringInfo(str, " :" CppAsString(fldname) " " format, node->fldname)
+/* Write a float field (actually, they're double) */
+#define WRITE_FLOAT_FIELD(fldname) \
+	(appendStringInfo(str, " :" CppAsString(fldname) " "), \
+	 outDouble(str, node->fldname))
 
 /* Write a boolean field */
 #define WRITE_BOOL_FIELD(fldname) \
@@ -198,6 +201,18 @@ outChar(StringInfo str, char c)
 	outToken(str, in);
 }
 
+/*
+ * Convert a double value, attempting to ensure the value is preserved exactly.
+ */
+static void
+outDouble(StringInfo str, double d)
+{
+	char		buf[DOUBLE_SHORTEST_DECIMAL_LEN];
+
+	double_to_shortest_decimal_buf(d, buf);
+	appendStringInfoString(str, buf);
+}
+
 /*
  * common implementation for scalar-array-writing functions
  *
@@ -525,7 +540,7 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
 			break;
 		case RTE_NAMEDTUPLESTORE:
 			WRITE_STRING_FIELD(enrname);
-			WRITE_FLOAT_FIELD(enrtuples, "%.0f");
+			WRITE_FLOAT_FIELD(enrtuples);
 			WRITE_OID_FIELD(relid);
 			WRITE_NODE_FIELD(coltypes);
 			WRITE_NODE_FIELD(coltypmods);
