to_char(float4/8): zero pad to specified length
authorBruce Momjian <bruce@momjian.us>
Sun, 22 Mar 2015 01:43:15 +0000 (21:43 -0400)
committerBruce Momjian <bruce@momjian.us>
Sun, 22 Mar 2015 01:43:36 +0000 (21:43 -0400)
Previously, zero padding was limited to the internal length, rather than
the specified length.  This allows it to match to_char(int/numeric), which
always padded to the specified length.

Regression tests added.

BACKWARD INCOMPATIBILITY

src/backend/utils/adt/formatting.c
src/test/regress/expected/numeric.out
src/test/regress/expected/window.out
src/test/regress/sql/numeric.sql

index 40a353f5134cb7766ed567ef9b975bb530b1f6fe..25b247ee78de7dbe243852139c08e11536a46c62 100644 (file)
 #define DCH_MAX_ITEM_SIZ      12       /* max localized day name       */
 #define NUM_MAX_ITEM_SIZ       8       /* roman number (RN has 15 chars)   */
 
-/* ----------
- * More is in float.c
- * ----------
- */
-#define MAXFLOATWIDTH  60
-#define MAXDOUBLEWIDTH 500
-
 
 /* ----------
  * Format parser structs
@@ -5214,8 +5207,7 @@ int4_to_char(PG_FUNCTION_ARGS)
        /* we can do it easily because float8 won't lose any precision */
        float8      val = (float8) value;
 
-       orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
-       snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, val);
+       orgnum = psprintf("%+.*e", Num.post, val);
 
        /*
         * Swap a leading positive sign for a space.
@@ -5414,7 +5406,6 @@ float4_to_char(PG_FUNCTION_ARGS)
        numstr = orgnum = int_to_roman((int) rint(value));
    else if (IS_EEEE(&Num))
    {
-       numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
        if (isnan(value) || is_infinite(value))
        {
            /*
@@ -5428,15 +5419,29 @@ float4_to_char(PG_FUNCTION_ARGS)
        }
        else
        {
-           snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);
+           numstr = psprintf("%+.*e", Num.post, value);
+
+           /* prevent the display of imprecise/junk digits */
+           if (Num.pre + Num.post > FLT_DIG)
+           {
+               int     digits = 0;
+               char   *numstr_p;
+
+               for (numstr_p = numstr; *numstr_p && *numstr_p != 'e'; numstr_p++)
+               {
+                   if (isdigit(*numstr_p))
+                   {
+                       if (++digits > FLT_DIG)
+                           *numstr_p = '0';
+                   }
+               }
+           }
 
            /*
             * Swap a leading positive sign for a space.
             */
-           if (*orgnum == '+')
-               *orgnum = ' ';
-
-           numstr = orgnum;
+           if (*numstr == '+')
+               *numstr = ' ';
        }
    }
    else
@@ -5452,16 +5457,24 @@ float4_to_char(PG_FUNCTION_ARGS)
            Num.pre += Num.multi;
        }
 
-       orgnum = (char *) palloc(MAXFLOATWIDTH + 1);
-       snprintf(orgnum, MAXFLOATWIDTH + 1, "%.0f", fabs(val));
-       numstr_pre_len = strlen(orgnum);
+       /* let psprintf() do the rounding */
+       orgnum = psprintf("%.*f", Num.post, val);
 
-       /* adjust post digits to fit max float digits */
-       if (numstr_pre_len >= FLT_DIG)
-           Num.post = 0;
-       else if (numstr_pre_len + Num.post > FLT_DIG)
-           Num.post = FLT_DIG - numstr_pre_len;
-       snprintf(orgnum, MAXFLOATWIDTH + 1, "%.*f", Num.post, val);
+       /* prevent the display of imprecise/junk digits */
+       if (Num.pre + Num.post > FLT_DIG)
+       {
+           int     digits = 0;
+           char   *orgnum_p;
+
+           for (orgnum_p = orgnum; *orgnum_p; orgnum_p++)
+           {
+               if (isdigit(*orgnum_p))
+               {
+                   if (++digits > FLT_DIG)
+                       *orgnum_p = '0';
+               }
+           }
+       }
 
        if (*orgnum == '-')
        {                       /* < 0 */
@@ -5520,7 +5533,6 @@ float8_to_char(PG_FUNCTION_ARGS)
        numstr = orgnum = int_to_roman((int) rint(value));
    else if (IS_EEEE(&Num))
    {
-       numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
        if (isnan(value) || is_infinite(value))
        {
            /*
@@ -5534,15 +5546,29 @@ float8_to_char(PG_FUNCTION_ARGS)
        }
        else
        {
-           snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, value);
+           numstr = psprintf("%+.*e", Num.post, value);
+
+           /* prevent the display of imprecise/junk digits */
+           if (Num.pre + Num.post > DBL_DIG)
+           {
+               int     digits = 0;
+               char   *numstr_p;
+
+               for (numstr_p = numstr; *numstr_p && *numstr_p != 'e'; numstr_p++)
+               {
+                   if (isdigit(*numstr_p))
+                   {
+                       if (++digits > DBL_DIG)
+                           *numstr_p = '0';
+                   }
+               }
+           }
 
            /*
             * Swap a leading positive sign for a space.
             */
-           if (*orgnum == '+')
-               *orgnum = ' ';
-
-           numstr = orgnum;
+           if (*numstr == '+')
+               *numstr = ' ';
        }
    }
    else
@@ -5557,15 +5583,25 @@ float8_to_char(PG_FUNCTION_ARGS)
            val = value * multi;
            Num.pre += Num.multi;
        }
-       orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
-       numstr_pre_len = snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.0f", fabs(val));
-
-       /* adjust post digits to fit max double digits */
-       if (numstr_pre_len >= DBL_DIG)
-           Num.post = 0;
-       else if (numstr_pre_len + Num.post > DBL_DIG)
-           Num.post = DBL_DIG - numstr_pre_len;
-       snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.*f", Num.post, val);
+
+       /* let psprintf() do the rounding */
+       orgnum = psprintf("%.*f", Num.post, val);
+
+       /* prevent the display of imprecise/junk digits */
+       if (Num.pre + Num.post > DBL_DIG)
+       {
+           int     digits = 0;
+           char   *orgnum_p;
+
+           for (orgnum_p = orgnum; *orgnum_p; orgnum_p++)
+           {
+               if (isdigit(*orgnum_p))
+               {
+                   if (++digits > DBL_DIG)
+                       *orgnum_p = '0';
+               }
+           }
+       }
 
        if (*orgnum == '-')
        {                       /* < 0 */
index 9d6814564df961c4dd4e6d449db3d32a9efe0894..46b0c72b6a340c9e97dddab7c19d943e7fb26427 100644 (file)
@@ -1499,3 +1499,73 @@ select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,5,i)
  3 | 4
 (10 rows)
 
+--
+-- Test code path for high-precision output
+--
+SELECT to_char(float8 '99999999999', '9999999999999999D99999999');
+          to_char           
+----------------------------
+       99999999999.00000000
+(1 row)
+
+SELECT to_char(float8 '99999999999', '9999999999999999D' || repeat('9', 1000));
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          to_char                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+       99999999999.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT to_char(float8 '1e9','999999999999999999999D9');
+         to_char          
+--------------------------
+             1000000000.0
+(1 row)
+
+SELECT to_char(float8 '1e20','999999999999999999999D9');
+         to_char          
+--------------------------
+  100000000000000000000.0
+(1 row)
+
+SELECT to_char(1e20, '999999999999999999999D9');
+         to_char          
+--------------------------
+  100000000000000000000.0
+(1 row)
+
+SELECT to_char(float8 '1.123456789123456789', '9.' || repeat('9', 55));
+                          to_char                           
+------------------------------------------------------------
+  1.1234567891234500000000000000000000000000000000000000000
+(1 row)
+
+SELECT to_char(float8 '1999999999999999999999999999999999999999999999.123456789123456789',
+        repeat('9', 50) || '.' || repeat('9', 50));
+                                                to_char                                                 
+--------------------------------------------------------------------------------------------------------
+      1999999999999990000000000000000000000000000000.00000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT to_char(float8 '0.1', '9D' || repeat('9', 1000));
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   to_char                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+   .1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+SELECT to_char(int4 '1', '9D' || repeat('9', 1000) || 'EEEE');
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     to_char                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e+00
+(1 row)
+
+SELECT to_char(float4 '1', '9D' || repeat('9', 1000) || 'EEEE');
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     to_char                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e+00
+(1 row)
+
+SELECT to_char(float8 '1', '9D' || repeat('9', 1000) || 'EEEE');
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     to_char                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e+00
+(1 row)
+
index 19f909f3d105087c2babcfa62ff3a77d442a3b03..79e65f6e6bbe0fc6d8cc898ef41a4734d934ee17 100644 (file)
@@ -1806,7 +1806,7 @@ SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FO
   FROM (VALUES(1,1e20),(2,1)) n(i,n);
          to_char          
 --------------------------
-  100000000000000000000
+  100000000000000000000.0
                       1.0
 (2 rows)
 
index 1633e4c375244f35b517d7a699b2a7c8ea7d3225..a6301eab0a4096478e43768e16869e4b14aa3137 100644 (file)
@@ -858,3 +858,20 @@ select (i / (10::numeric ^ 131071))::numeric(1,0)
 select * from generate_series(1::numeric, 3::numeric) i, generate_series(i,3) j;
 select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,i) j;
 select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,5,i) j;
+
+--
+-- Test code path for high-precision output
+--
+
+SELECT to_char(float8 '99999999999', '9999999999999999D99999999');
+SELECT to_char(float8 '99999999999', '9999999999999999D' || repeat('9', 1000));
+SELECT to_char(float8 '1e9','999999999999999999999D9');
+SELECT to_char(float8 '1e20','999999999999999999999D9');
+SELECT to_char(1e20, '999999999999999999999D9');
+SELECT to_char(float8 '1.123456789123456789', '9.' || repeat('9', 55));
+SELECT to_char(float8 '1999999999999999999999999999999999999999999999.123456789123456789',
+        repeat('9', 50) || '.' || repeat('9', 50));
+SELECT to_char(float8 '0.1', '9D' || repeat('9', 1000));
+SELECT to_char(int4 '1', '9D' || repeat('9', 1000) || 'EEEE');
+SELECT to_char(float4 '1', '9D' || repeat('9', 1000) || 'EEEE');
+SELECT to_char(float8 '1', '9D' || repeat('9', 1000) || 'EEEE');