Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit decbe2b

Browse files
committed
Fix behavior of exp() and power() for infinity inputs.
Previously, these functions tended to throw underflow errors for negative-infinity exponents. The correct thing per POSIX is to return 0, so let's do that instead. (Note that the SQL standard is silent on such issues, as it lacks the concepts of either Inf or NaN; so our practice is to follow POSIX whenever a corresponding C-library function exists.) Also, add a bunch of test cases verifying that exp() and power() actually do follow POSIX for Inf and NaN inputs. While this patch should guarantee that exp() passes the tests, power() will not unless the platform's pow(3) is fully POSIX-compliant. I already know that gaur fails some of the tests, and I am suspicious that the Windows animals will too; the extent of compliance of other old platforms remains to be seen. We might choose to drop failing test cases, or to work harder at overriding pow(3) for these cases, but first let's see just how good or bad the situation is. Discussion: https://postgr.es/m/582552.1591917752@sss.pgh.pa.us
1 parent 378badc commit decbe2b

File tree

3 files changed

+221
-10
lines changed

3 files changed

+221
-10
lines changed

src/backend/utils/adt/float.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,7 +1565,7 @@ dpow(PG_FUNCTION_ARGS)
15651565

15661566
if (unlikely(isinf(result)) && !isinf(arg1) && !isinf(arg2))
15671567
float_overflow_error();
1568-
if (unlikely(result == 0.0) && arg1 != 0.0)
1568+
if (unlikely(result == 0.0) && arg1 != 0.0 && !isinf(arg1) && !isinf(arg2))
15691569
float_underflow_error();
15701570

15711571
PG_RETURN_FLOAT8(result);
@@ -1581,15 +1581,38 @@ dexp(PG_FUNCTION_ARGS)
15811581
float8 arg1 = PG_GETARG_FLOAT8(0);
15821582
float8 result;
15831583

1584-
errno = 0;
1585-
result = exp(arg1);
1586-
if (errno == ERANGE && result != 0 && !isinf(result))
1587-
result = get_float8_infinity();
1588-
1589-
if (unlikely(isinf(result)) && !isinf(arg1))
1590-
float_overflow_error();
1591-
if (unlikely(result == 0.0))
1592-
float_underflow_error();
1584+
/*
1585+
* Handle NaN and Inf cases explicitly. This avoids needing to assume
1586+
* that the platform's exp() conforms to POSIX for these cases, and it
1587+
* removes some edge cases for the overflow checks below.
1588+
*/
1589+
if (isnan(arg1))
1590+
result = arg1;
1591+
else if (isinf(arg1))
1592+
{
1593+
/* Per POSIX, exp(-Inf) is 0 */
1594+
result = (arg1 > 0.0) ? arg1 : 0;
1595+
}
1596+
else
1597+
{
1598+
/*
1599+
* On some platforms, exp() will not set errno but just return Inf or
1600+
* zero to report overflow/underflow; therefore, test both cases.
1601+
*/
1602+
errno = 0;
1603+
result = exp(arg1);
1604+
if (unlikely(errno == ERANGE))
1605+
{
1606+
if (result != 0.0)
1607+
float_overflow_error();
1608+
else
1609+
float_underflow_error();
1610+
}
1611+
else if (unlikely(isinf(result)))
1612+
float_overflow_error();
1613+
else if (unlikely(result == 0.0))
1614+
float_underflow_error();
1615+
}
15931616

15941617
PG_RETURN_FLOAT8(result);
15951618
}

src/test/regress/expected/float8.out

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,158 @@ SELECT power(float8 'NaN', float8 '0');
385385
1
386386
(1 row)
387387

388+
SELECT power(float8 'inf', float8 '0');
389+
power
390+
-------
391+
1
392+
(1 row)
393+
394+
SELECT power(float8 '-inf', float8 '0');
395+
power
396+
-------
397+
1
398+
(1 row)
399+
400+
SELECT power(float8 '0', float8 'inf');
401+
power
402+
-------
403+
0
404+
(1 row)
405+
406+
SELECT power(float8 '0', float8 '-inf');
407+
ERROR: zero raised to a negative power is undefined
408+
SELECT power(float8 '1', float8 'inf');
409+
power
410+
-------
411+
1
412+
(1 row)
413+
414+
SELECT power(float8 '1', float8 '-inf');
415+
power
416+
-------
417+
1
418+
(1 row)
419+
420+
SELECT power(float8 '-1', float8 'inf');
421+
power
422+
-------
423+
1
424+
(1 row)
425+
426+
SELECT power(float8 '-1', float8 '-inf');
427+
power
428+
-------
429+
1
430+
(1 row)
431+
432+
SELECT power(float8 '0.1', float8 'inf');
433+
power
434+
-------
435+
0
436+
(1 row)
437+
438+
SELECT power(float8 '-0.1', float8 'inf');
439+
power
440+
-------
441+
0
442+
(1 row)
443+
444+
SELECT power(float8 '1.1', float8 'inf');
445+
power
446+
----------
447+
Infinity
448+
(1 row)
449+
450+
SELECT power(float8 '-1.1', float8 'inf');
451+
power
452+
----------
453+
Infinity
454+
(1 row)
455+
456+
SELECT power(float8 '0.1', float8 '-inf');
457+
power
458+
----------
459+
Infinity
460+
(1 row)
461+
462+
SELECT power(float8 '-0.1', float8 '-inf');
463+
power
464+
----------
465+
Infinity
466+
(1 row)
467+
468+
SELECT power(float8 '1.1', float8 '-inf');
469+
power
470+
-------
471+
0
472+
(1 row)
473+
474+
SELECT power(float8 '-1.1', float8 '-inf');
475+
power
476+
-------
477+
0
478+
(1 row)
479+
480+
SELECT power(float8 'inf', float8 '-2');
481+
power
482+
-------
483+
0
484+
(1 row)
485+
486+
SELECT power(float8 'inf', float8 '2');
487+
power
488+
----------
489+
Infinity
490+
(1 row)
491+
492+
SELECT power(float8 'inf', float8 'inf');
493+
power
494+
----------
495+
Infinity
496+
(1 row)
497+
498+
SELECT power(float8 'inf', float8 '-inf');
499+
power
500+
-------
501+
0
502+
(1 row)
503+
504+
SELECT power(float8 '-inf', float8 '-2');
505+
power
506+
-------
507+
0
508+
(1 row)
509+
510+
SELECT power(float8 '-inf', float8 '-3');
511+
power
512+
-------
513+
-0
514+
(1 row)
515+
516+
SELECT power(float8 '-inf', float8 '2');
517+
power
518+
----------
519+
Infinity
520+
(1 row)
521+
522+
SELECT power(float8 '-inf', float8 '3');
523+
power
524+
-----------
525+
-Infinity
526+
(1 row)
527+
528+
SELECT power(float8 '-inf', float8 'inf');
529+
power
530+
----------
531+
Infinity
532+
(1 row)
533+
534+
SELECT power(float8 '-inf', float8 '-inf');
535+
power
536+
-------
537+
0
538+
(1 row)
539+
388540
-- take exp of ln(f.f1)
389541
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
390542
FROM FLOAT8_TBL f
@@ -396,6 +548,13 @@ SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
396548
| 1.2345678901234e-200 | 1.23456789012339e-200
397549
(3 rows)
398550

551+
-- check edge cases for exp
552+
SELECT exp('inf'::float8), exp('-inf'::float8), exp('nan'::float8);
553+
exp | exp | exp
554+
----------+-----+-----
555+
Infinity | 0 | NaN
556+
(1 row)
557+
399558
-- cube root
400559
SELECT ||/ float8 '27' AS three;
401560
three

src/test/regress/sql/float8.sql

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,41 @@ SELECT power(float8 'NaN', float8 'NaN');
120120
SELECT power(float8 '-1', float8 'NaN');
121121
SELECT power(float8 '1', float8 'NaN');
122122
SELECT power(float8 'NaN', float8 '0');
123+
SELECT power(float8 'inf', float8 '0');
124+
SELECT power(float8 '-inf', float8 '0');
125+
SELECT power(float8 '0', float8 'inf');
126+
SELECT power(float8 '0', float8 '-inf');
127+
SELECT power(float8 '1', float8 'inf');
128+
SELECT power(float8 '1', float8 '-inf');
129+
SELECT power(float8 '-1', float8 'inf');
130+
SELECT power(float8 '-1', float8 '-inf');
131+
SELECT power(float8 '0.1', float8 'inf');
132+
SELECT power(float8 '-0.1', float8 'inf');
133+
SELECT power(float8 '1.1', float8 'inf');
134+
SELECT power(float8 '-1.1', float8 'inf');
135+
SELECT power(float8 '0.1', float8 '-inf');
136+
SELECT power(float8 '-0.1', float8 '-inf');
137+
SELECT power(float8 '1.1', float8 '-inf');
138+
SELECT power(float8 '-1.1', float8 '-inf');
139+
SELECT power(float8 'inf', float8 '-2');
140+
SELECT power(float8 'inf', float8 '2');
141+
SELECT power(float8 'inf', float8 'inf');
142+
SELECT power(float8 'inf', float8 '-inf');
143+
SELECT power(float8 '-inf', float8 '-2');
144+
SELECT power(float8 '-inf', float8 '-3');
145+
SELECT power(float8 '-inf', float8 '2');
146+
SELECT power(float8 '-inf', float8 '3');
147+
SELECT power(float8 '-inf', float8 'inf');
148+
SELECT power(float8 '-inf', float8 '-inf');
123149

124150
-- take exp of ln(f.f1)
125151
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
126152
FROM FLOAT8_TBL f
127153
WHERE f.f1 > '0.0';
128154

155+
-- check edge cases for exp
156+
SELECT exp('inf'::float8), exp('-inf'::float8), exp('nan'::float8);
157+
129158
-- cube root
130159
SELECT ||/ float8 '27' AS three;
131160

0 commit comments

Comments
 (0)