Commit cc0d90b7 authored by Bruce Momjian's avatar Bruce Momjian

to_char(float4/8): zero pad to specified length

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
parent 1933a5bb
...@@ -113,13 +113,6 @@ ...@@ -113,13 +113,6 @@
#define DCH_MAX_ITEM_SIZ 12 /* max localized day name */ #define DCH_MAX_ITEM_SIZ 12 /* max localized day name */
#define NUM_MAX_ITEM_SIZ 8 /* roman number (RN has 15 chars) */ #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 * Format parser structs
...@@ -5214,8 +5207,7 @@ int4_to_char(PG_FUNCTION_ARGS) ...@@ -5214,8 +5207,7 @@ int4_to_char(PG_FUNCTION_ARGS)
/* we can do it easily because float8 won't lose any precision */ /* we can do it easily because float8 won't lose any precision */
float8 val = (float8) value; float8 val = (float8) value;
orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1); orgnum = psprintf("%+.*e", Num.post, val);
snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%+.*e", Num.post, val);
/* /*
* Swap a leading positive sign for a space. * Swap a leading positive sign for a space.
...@@ -5414,7 +5406,6 @@ float4_to_char(PG_FUNCTION_ARGS) ...@@ -5414,7 +5406,6 @@ float4_to_char(PG_FUNCTION_ARGS)
numstr = orgnum = int_to_roman((int) rint(value)); numstr = orgnum = int_to_roman((int) rint(value));
else if (IS_EEEE(&Num)) else if (IS_EEEE(&Num))
{ {
numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
if (isnan(value) || is_infinite(value)) if (isnan(value) || is_infinite(value))
{ {
/* /*
...@@ -5428,15 +5419,29 @@ float4_to_char(PG_FUNCTION_ARGS) ...@@ -5428,15 +5419,29 @@ float4_to_char(PG_FUNCTION_ARGS)
} }
else 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. * Swap a leading positive sign for a space.
*/ */
if (*orgnum == '+') if (*numstr == '+')
*orgnum = ' '; *numstr = ' ';
numstr = orgnum;
} }
} }
else else
...@@ -5452,16 +5457,24 @@ float4_to_char(PG_FUNCTION_ARGS) ...@@ -5452,16 +5457,24 @@ float4_to_char(PG_FUNCTION_ARGS)
Num.pre += Num.multi; Num.pre += Num.multi;
} }
orgnum = (char *) palloc(MAXFLOATWIDTH + 1); /* let psprintf() do the rounding */
snprintf(orgnum, MAXFLOATWIDTH + 1, "%.0f", fabs(val)); orgnum = psprintf("%.*f", Num.post, val);
numstr_pre_len = strlen(orgnum);
/* adjust post digits to fit max float digits */ /* prevent the display of imprecise/junk digits */
if (numstr_pre_len >= FLT_DIG) if (Num.pre + Num.post > FLT_DIG)
Num.post = 0; {
else if (numstr_pre_len + Num.post > FLT_DIG) int digits = 0;
Num.post = FLT_DIG - numstr_pre_len; char *orgnum_p;
snprintf(orgnum, MAXFLOATWIDTH + 1, "%.*f", Num.post, val);
for (orgnum_p = orgnum; *orgnum_p; orgnum_p++)
{
if (isdigit(*orgnum_p))
{
if (++digits > FLT_DIG)
*orgnum_p = '0';
}
}
}
if (*orgnum == '-') if (*orgnum == '-')
{ /* < 0 */ { /* < 0 */
...@@ -5520,7 +5533,6 @@ float8_to_char(PG_FUNCTION_ARGS) ...@@ -5520,7 +5533,6 @@ float8_to_char(PG_FUNCTION_ARGS)
numstr = orgnum = int_to_roman((int) rint(value)); numstr = orgnum = int_to_roman((int) rint(value));
else if (IS_EEEE(&Num)) else if (IS_EEEE(&Num))
{ {
numstr = orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
if (isnan(value) || is_infinite(value)) if (isnan(value) || is_infinite(value))
{ {
/* /*
...@@ -5534,15 +5546,29 @@ float8_to_char(PG_FUNCTION_ARGS) ...@@ -5534,15 +5546,29 @@ float8_to_char(PG_FUNCTION_ARGS)
} }
else 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. * Swap a leading positive sign for a space.
*/ */
if (*orgnum == '+') if (*numstr == '+')
*orgnum = ' '; *numstr = ' ';
numstr = orgnum;
} }
} }
else else
...@@ -5557,15 +5583,25 @@ float8_to_char(PG_FUNCTION_ARGS) ...@@ -5557,15 +5583,25 @@ float8_to_char(PG_FUNCTION_ARGS)
val = value * multi; val = value * multi;
Num.pre += Num.multi; Num.pre += Num.multi;
} }
orgnum = (char *) palloc(MAXDOUBLEWIDTH + 1);
numstr_pre_len = snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.0f", fabs(val)); /* let psprintf() do the rounding */
orgnum = psprintf("%.*f", Num.post, val);
/* adjust post digits to fit max double digits */
if (numstr_pre_len >= DBL_DIG) /* prevent the display of imprecise/junk digits */
Num.post = 0; if (Num.pre + Num.post > DBL_DIG)
else if (numstr_pre_len + Num.post > DBL_DIG) {
Num.post = DBL_DIG - numstr_pre_len; int digits = 0;
snprintf(orgnum, MAXDOUBLEWIDTH + 1, "%.*f", Num.post, val); char *orgnum_p;
for (orgnum_p = orgnum; *orgnum_p; orgnum_p++)
{
if (isdigit(*orgnum_p))
{
if (++digits > DBL_DIG)
*orgnum_p = '0';
}
}
}
if (*orgnum == '-') if (*orgnum == '-')
{ /* < 0 */ { /* < 0 */
......
This diff is collapsed.
...@@ -1806,7 +1806,7 @@ SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FO ...@@ -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); FROM (VALUES(1,1e20),(2,1)) n(i,n);
to_char to_char
-------------------------- --------------------------
100000000000000000000 100000000000000000000.0
1.0 1.0
(2 rows) (2 rows)
......
...@@ -858,3 +858,20 @@ select (i / (10::numeric ^ 131071))::numeric(1,0) ...@@ -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(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,i) j;
select * from generate_series(1::numeric, 3::numeric) i, generate_series(1,5,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');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment