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

Commita72ad63

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 parent47a573d commita72ad63

File tree

3 files changed

+78
-29
lines changed

3 files changed

+78
-29
lines changed

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

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

386-
#ifDEC_DIGITS==4||DEC_DIGITS==2
387-
staticconstNumericDigitconst_ten_data[1]= {10};
388-
staticconstNumericVarconst_ten=
389-
{1,0,NUMERIC_POS,0,NULL, (NumericDigit*)const_ten_data};
390-
#elifDEC_DIGITS==1
391-
staticconstNumericDigitconst_ten_data[1]= {1};
392-
staticconstNumericVarconst_ten=
393-
{1,1,NUMERIC_POS,0,NULL, (NumericDigit*)const_ten_data};
394-
#endif
395-
396386
#ifDEC_DIGITS==4
397387
staticconstNumericDigitconst_zero_point_nine_data[1]= {9000};
398388
#elifDEC_DIGITS==2
@@ -526,6 +516,7 @@ static void power_var(const NumericVar *base, const NumericVar *exp,
526516
NumericVar*result);
527517
staticvoidpower_var_int(constNumericVar*base,intexp,NumericVar*result,
528518
intrscale);
519+
staticvoidpower_ten_int(intexp,NumericVar*result);
529520

530521
staticintcmp_abs(constNumericVar*var1,constNumericVar*var2);
531522
staticintcmp_abs_common(constNumericDigit*var1digits,intvar1ndigits,
@@ -6354,9 +6345,7 @@ static char *
63546345
get_str_from_var_sci(constNumericVar*var,intrscale)
63556346
{
63566347
int32exponent;
6357-
NumericVardenominator;
6358-
NumericVarsignificand;
6359-
intdenom_scale;
6348+
NumericVartmp_var;
63606349
size_tlen;
63616350
char*str;
63626351
char*sig_out;
@@ -6393,25 +6382,16 @@ get_str_from_var_sci(const NumericVar *var, int rscale)
63936382
}
63946383

63956384
/*
6396-
* The denominator is set to 10 raised to the power of the exponent.
6397-
*
6398-
* We then divide var by the denominator to get the significand, rounding
6399-
* to rscale decimal digits in the process.
6385+
* Divide var by 10^exponent to get the significand, rounding to rscale
6386+
* decimal digits in the process.
64006387
*/
6401-
if (exponent<0)
6402-
denom_scale=-exponent;
6403-
else
6404-
denom_scale=0;
6405-
6406-
init_var(&denominator);
6407-
init_var(&significand);
6388+
init_var(&tmp_var);
64086389

6409-
power_var_int(&const_ten,exponent,&denominator,denom_scale);
6410-
div_var(var,&denominator,&significand,rscale, true);
6411-
sig_out=get_str_from_var(&significand);
6390+
power_ten_int(exponent,&tmp_var);
6391+
div_var(var,&tmp_var,&tmp_var,rscale, true);
6392+
sig_out=get_str_from_var(&tmp_var);
64126393

6413-
free_var(&denominator);
6414-
free_var(&significand);
6394+
free_var(&tmp_var);
64156395

64166396
/*
64176397
* Allocate space for the result.
@@ -9498,6 +9478,34 @@ power_var_int(const NumericVar *base, int exp, NumericVar *result, int rscale)
94989478
round_var(result,rscale);
94999479
}
95009480

9481+
/*
9482+
* power_ten_int() -
9483+
*
9484+
*Raise ten to the power of exp, where exp is an integer. Note that unlike
9485+
*power_var_int(), this does no overflow/underflow checking or rounding.
9486+
*/
9487+
staticvoid
9488+
power_ten_int(intexp,NumericVar*result)
9489+
{
9490+
/* Construct the result directly, starting from 10^0 = 1 */
9491+
set_var_from_var(&const_one,result);
9492+
9493+
/* Scale needed to represent the result exactly */
9494+
result->dscale=exp<0 ?-exp :0;
9495+
9496+
/* Base-NBASE weight of result and remaining exponent */
9497+
if (exp >=0)
9498+
result->weight=exp /DEC_DIGITS;
9499+
else
9500+
result->weight= (exp+1) /DEC_DIGITS-1;
9501+
9502+
exp-=result->weight*DEC_DIGITS;
9503+
9504+
/* Final adjustment of the result's single NBASE digit */
9505+
while (exp-->0)
9506+
result->digits[0] *=10;
9507+
}
9508+
95019509

95029510
/* ----------------------------------------------------------------------
95039511
*

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,39 @@ SELECT '' AS to_char_36, to_char('100'::numeric, 'f"ool\\"999');
12781278
| fool\ 100
12791279
(1 row)
12801280

1281+
-- Test scientific notation with various exponents
1282+
WITH v(exp) AS
1283+
(VALUES(-16379),(-16378),(-1234),(-789),(-45),(-5),(-4),(-3),(-2),(-1),(0),
1284+
(1),(2),(3),(4),(5),(38),(275),(2345),(45678),(131070),(131071))
1285+
SELECT exp,
1286+
to_char(('1.2345e'||exp)::numeric, '9.999EEEE') as numeric
1287+
FROM v;
1288+
exp | numeric
1289+
--------+----------------
1290+
-16379 | 1.235e-16379
1291+
-16378 | 1.235e-16378
1292+
-1234 | 1.235e-1234
1293+
-789 | 1.235e-789
1294+
-45 | 1.235e-45
1295+
-5 | 1.235e-05
1296+
-4 | 1.235e-04
1297+
-3 | 1.235e-03
1298+
-2 | 1.235e-02
1299+
-1 | 1.235e-01
1300+
0 | 1.235e+00
1301+
1 | 1.235e+01
1302+
2 | 1.235e+02
1303+
3 | 1.235e+03
1304+
4 | 1.235e+04
1305+
5 | 1.235e+05
1306+
38 | 1.235e+38
1307+
275 | 1.235e+275
1308+
2345 | 1.235e+2345
1309+
45678 | 1.235e+45678
1310+
131070 | 1.235e+131070
1311+
131071 | 1.235e+131071
1312+
(22 rows)
1313+
12811314
-- TO_NUMBER()
12821315
--
12831316
SET lc_numeric = 'C';

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,14 @@ SELECT '' AS to_char_34, to_char('100'::numeric, 'f"\\ool"999');
798798
SELECT''AS to_char_35, to_char('100'::numeric,'f"ool\"999');
799799
SELECT''AS to_char_36, to_char('100'::numeric,'f"ool\\"999');
800800

801+
-- Test scientific notation with various exponents
802+
WITH v(exp)AS
803+
(VALUES(-16379),(-16378),(-1234),(-789),(-45),(-5),(-4),(-3),(-2),(-1),(0),
804+
(1),(2),(3),(4),(5),(38),(275),(2345),(45678),(131070),(131071))
805+
SELECT exp,
806+
to_char(('1.2345e'||exp)::numeric,'9.999EEEE')asnumeric
807+
FROM v;
808+
801809
-- TO_NUMBER()
802810
--
803811
SET lc_numeric='C';

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp