aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/adt/formatting.c114
-rw-r--r--src/test/regress/expected/numeric.out70
-rw-r--r--src/test/regress/expected/window.out2
-rw-r--r--src/test/regress/sql/numeric.sql17
4 files changed, 163 insertions, 40 deletions
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index 40a353f5134..25b247ee78d 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -113,13 +113,6 @@
#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 */
diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out
index 9d6814564df..46b0c72b6a3 100644
--- a/src/test/regress/expected/numeric.out
+++ b/src/test/regress/expected/numeric.out
@@ -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)
+
diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out
index 19f909f3d10..79e65f6e6bb 100644
--- a/src/test/regress/expected/window.out
+++ b/src/test/regress/expected/window.out
@@ -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)
diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql
index 1633e4c3752..a6301eab0a4 100644
--- a/src/test/regress/sql/numeric.sql
+++ b/src/test/regress/sql/numeric.sql
@@ -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');