Don't lose precision for float fields of Nodes.
authorPeter Eisentraut <peter@eisentraut.org>
Mon, 26 Sep 2022 14:02:09 +0000 (16:02 +0200)
committerPeter Eisentraut <peter@eisentraut.org>
Mon, 26 Sep 2022 14:02:09 +0000 (16:02 +0200)
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.

Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://www.postgresql.org/message-id/flat/4159834.1657405226@sss.pgh.pa.us

src/backend/nodes/gen_node_support.pl
src/backend/nodes/outfuncs.c

index b707a09f565b1a15b6fcf261dcf1936da2cae4d0..81b8c184a90bb52a3474d547e4a75d73fbf61042 100644 (file)
@@ -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*')
index 63dda75ae52e72a35c228c2bef6207bb2c01a6b8..64c65f060b4ad786337a6bbaa4e1f9d7ce5828db 100644 (file)
@@ -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);