Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit226ec49

Browse files
committed
Fix division-by-zero error in to_char() with 'EEEE' format.
This fixes a long-standing bug when using to_char() to format anumeric value in scientific notation -- if the value's exponent isless than -NUMERIC_MAX_DISPLAY_SCALE-1 (-1001), it produced adivision-by-zero error.The reason for this error was that get_str_from_var_sci() divides itsinput by 10^exp, which it produced using power_var_int(). However, theunderflow test in power_var_int() causes it to return zero if theresult scale is too small. That's not a problem for power_var_int()'sonly other caller, power_var(), since that limits the rscale to 1000,but in get_str_from_var_sci() the exponent can be much smaller,requiring a much larger rscale. Fix by introducing a new function tocompute 10^exp directly, with no rscale limit. This also allows 10^expto be computed more efficiently, without any numeric multiplication,division or rounding.Discussion:https://postgr.es/m/CAEZATCWhojfH4whaqgUKBe8D5jNHB8ytzemL-PnRx+KCTyMXmg@mail.gmail.com
1 parent87bff68 commit226ec49

File tree

3 files changed

+76
-29
lines changed

3 files changed

+76
-29
lines changed

‎src/backend/utils/adt/numeric.c

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -428,16 +428,6 @@ static const NumericDigit const_two_data[1] = {2};
428428
staticconstNumericVarconst_two=
429429
{1,0,NUMERIC_POS,0,NULL, (NumericDigit*)const_two_data};
430430

431-
#ifDEC_DIGITS==4||DEC_DIGITS==2
432-
staticconstNumericDigitconst_ten_data[1]= {10};
433-
staticconstNumericVarconst_ten=
434-
{1,0,NUMERIC_POS,0,NULL, (NumericDigit*)const_ten_data};
435-
#elifDEC_DIGITS==1
436-
staticconstNumericDigitconst_ten_data[1]= {1};
437-
staticconstNumericVarconst_ten=
438-
{1,1,NUMERIC_POS,0,NULL, (NumericDigit*)const_ten_data};
439-
#endif
440-
441431
#ifDEC_DIGITS==4
442432
staticconstNumericDigitconst_zero_point_nine_data[1]= {9000};
443433
#elifDEC_DIGITS==2
@@ -582,6 +572,7 @@ static void power_var(const NumericVar *base, const NumericVar *exp,
582572
NumericVar*result);
583573
staticvoidpower_var_int(constNumericVar*base,intexp,NumericVar*result,
584574
intrscale);
575+
staticvoidpower_ten_int(intexp,NumericVar*result);
585576

586577
staticintcmp_abs(constNumericVar*var1,constNumericVar*var2);
587578
staticintcmp_abs_common(constNumericDigit*var1digits,intvar1ndigits,
@@ -7210,9 +7201,7 @@ static char *
72107201
get_str_from_var_sci(constNumericVar*var,intrscale)
72117202
{
72127203
int32exponent;
7213-
NumericVardenominator;
7214-
NumericVarsignificand;
7215-
intdenom_scale;
7204+
NumericVartmp_var;
72167205
size_tlen;
72177206
char*str;
72187207
char*sig_out;
@@ -7249,25 +7238,16 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
72497238
}
72507239

72517240
/*
7252-
* The denominator is set to 10 raised to the power of the exponent.
7253-
*
7254-
* We then divide var by the denominator to get the significand, rounding
7255-
* to rscale decimal digits in the process.
7241+
* Divide var by 10^exponent to get the significand, rounding to rscale
7242+
* decimal digits in the process.
72567243
*/
7257-
if (exponent<0)
7258-
denom_scale=-exponent;
7259-
else
7260-
denom_scale=0;
7261-
7262-
init_var(&denominator);
7263-
init_var(&significand);
7244+
init_var(&tmp_var);
72647245

7265-
power_var_int(&const_ten,exponent,&denominator,denom_scale);
7266-
div_var(var,&denominator,&significand,rscale, true);
7267-
sig_out=get_str_from_var(&significand);
7246+
power_ten_int(exponent,&tmp_var);
7247+
div_var(var,&tmp_var,&tmp_var,rscale, true);
7248+
sig_out=get_str_from_var(&tmp_var);
72687249

7269-
free_var(&denominator);
7270-
free_var(&significand);
7250+
free_var(&tmp_var);
72717251

72727252
/*
72737253
* Allocate space for the result.
@@ -10519,6 +10499,34 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
1051910499
round_var(result,rscale);
1052010500
}
1052110501

10502+
/*
10503+
* power_ten_int() -
10504+
*
10505+
*Raise ten to the power of exp, where exp is an integer. Note that unlike
10506+
*power_var_int(), this does no overflow/underflow checking or rounding.
10507+
*/
10508+
staticvoid
10509+
power_ten_int(intexp,NumericVar*result)
10510+
{
10511+
/* Construct the result directly, starting from 10^0 = 1 */
10512+
set_var_from_var(&const_one,result);
10513+
10514+
/* Scale needed to represent the result exactly */
10515+
result->dscale=exp<0 ?-exp :0;
10516+
10517+
/* Base-NBASE weight of result and remaining exponent */
10518+
if (exp >=0)
10519+
result->weight=exp /DEC_DIGITS;
10520+
else
10521+
result->weight= (exp+1) /DEC_DIGITS-1;
10522+
10523+
exp-=result->weight*DEC_DIGITS;
10524+
10525+
/* Final adjustment of the result's single NBASE digit */
10526+
while (exp-->0)
10527+
result->digits[0] *=10;
10528+
}
10529+
1052210530

1052310531
/* ----------------------------------------------------------------------
1052410532
*

‎src/test/regress/expected/numeric.out

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,38 @@ FROM v;
17941794
NaN | #.####### | #.####### | #.#######
17951795
(7 rows)
17961796

1797+
WITH v(exp) AS
1798+
(VALUES(-16379),(-16378),(-1234),(-789),(-45),(-5),(-4),(-3),(-2),(-1),(0),
1799+
(1),(2),(3),(4),(5),(38),(275),(2345),(45678),(131070),(131071))
1800+
SELECT exp,
1801+
to_char(('1.2345e'||exp)::numeric, '9.999EEEE') as numeric
1802+
FROM v;
1803+
exp | numeric
1804+
--------+----------------
1805+
-16379 | 1.235e-16379
1806+
-16378 | 1.235e-16378
1807+
-1234 | 1.235e-1234
1808+
-789 | 1.235e-789
1809+
-45 | 1.235e-45
1810+
-5 | 1.235e-05
1811+
-4 | 1.235e-04
1812+
-3 | 1.235e-03
1813+
-2 | 1.235e-02
1814+
-1 | 1.235e-01
1815+
0 | 1.235e+00
1816+
1 | 1.235e+01
1817+
2 | 1.235e+02
1818+
3 | 1.235e+03
1819+
4 | 1.235e+04
1820+
5 | 1.235e+05
1821+
38 | 1.235e+38
1822+
275 | 1.235e+275
1823+
2345 | 1.235e+2345
1824+
45678 | 1.235e+45678
1825+
131070 | 1.235e+131070
1826+
131071 | 1.235e+131071
1827+
(22 rows)
1828+
17971829
WITH v(val) AS
17981830
(VALUES('0'::numeric),('-4.2'),('4.2e9'),('1.2e-5'),('inf'),('-inf'),('nan'))
17991831
SELECT val,

‎src/test/regress/sql/numeric.sql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,13 @@ SELECT val,
939939
to_char(val::float4,'9.999EEEE')as float4
940940
FROM v;
941941

942+
WITH v(exp)AS
943+
(VALUES(-16379),(-16378),(-1234),(-789),(-45),(-5),(-4),(-3),(-2),(-1),(0),
944+
(1),(2),(3),(4),(5),(38),(275),(2345),(45678),(131070),(131071))
945+
SELECT exp,
946+
to_char(('1.2345e'||exp)::numeric,'9.999EEEE')asnumeric
947+
FROM v;
948+
942949
WITH v(val)AS
943950
(VALUES('0'::numeric),('-4.2'),('4.2e9'),('1.2e-5'),('inf'),('-inf'),('nan'))
944951
SELECT val,

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp