Skip to content

Commit 59c2df3

Browse files
committed
Avoid wrong results for power() with NaN input on more platforms.
Buildfarm results show that the modern POSIX rule that 1 ^ NaN = 1 is not honored on *BSD until relatively recently, and really old platforms don't believe that NaN ^ 0 = 1 either. (This is unsurprising, perhaps, since SUSv2 doesn't require either behavior.) In hopes of getting to platform independent behavior, let's deal with all the NaN-input cases explicitly in dpow(). Note that numeric_power() doesn't know either of these special cases. But since that behavior is platform-independent, I think it should be addressed separately, and probably not back-patched. Discussion: https://postgr.es/m/75DB81BEEA95B445AE6D576A0A5C9E936A73E741@BPXM05GP.gisp.nec.co.jp
1 parent 37c02b2 commit 59c2df3

File tree

6 files changed

+46
-3
lines changed

6 files changed

+46
-3
lines changed

src/backend/utils/adt/float.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,25 @@ dpow(PG_FUNCTION_ARGS)
13911391
float8 arg2 = PG_GETARG_FLOAT8(1);
13921392
float8 result;
13931393

1394+
/*
1395+
* The POSIX spec says that NaN ^ 0 = 1, and 1 ^ NaN = 1, while all other
1396+
* cases with NaN inputs yield NaN (with no error). Many older platforms
1397+
* get one or more of these cases wrong, so deal with them via explicit
1398+
* logic rather than trusting pow(3).
1399+
*/
1400+
if (isnan(arg1))
1401+
{
1402+
if (isnan(arg2) || arg2 != 0.0)
1403+
PG_RETURN_FLOAT8(get_float8_nan());
1404+
PG_RETURN_FLOAT8(1.0);
1405+
}
1406+
if (isnan(arg2))
1407+
{
1408+
if (arg1 != 1.0)
1409+
PG_RETURN_FLOAT8(get_float8_nan());
1410+
PG_RETURN_FLOAT8(1.0);
1411+
}
1412+
13941413
/*
13951414
* The SQL spec requires that we emit a particular SQLSTATE error code for
13961415
* certain error conditions. Specifically, we don't return a
@@ -1412,12 +1431,11 @@ dpow(PG_FUNCTION_ARGS)
14121431
* and result == NaN for negative arg1 and very large arg2 (they must be
14131432
* using something different from our floor() test to decide it's
14141433
* invalid). Other platforms (HPPA) return errno == ERANGE and a large
1415-
* (HUGE_VAL) but finite result to signal overflow. Also, some versions
1416-
* of MSVC return errno == EDOM and result == NaN for NaN inputs.
1434+
* (HUGE_VAL) but finite result to signal overflow.
14171435
*/
14181436
errno = 0;
14191437
result = pow(arg1, arg2);
1420-
if (errno == EDOM && isnan(result) && !isnan(arg1) && !isnan(arg2))
1438+
if (errno == EDOM && isnan(result))
14211439
{
14221440
if ((fabs(arg1) > 1 && arg2 >= 0) || (fabs(arg1) < 1 && arg2 < 0))
14231441
/* The sign of Inf is not significant in this case. */

src/test/regress/expected/float8-exp-three-digits-win32.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ SELECT power(float8 'NaN', float8 'NaN');
358358
NaN
359359
(1 row)
360360

361+
SELECT power(float8 '-1', float8 'NaN');
362+
power
363+
-------
364+
NaN
365+
(1 row)
366+
361367
SELECT power(float8 '1', float8 'NaN');
362368
power
363369
-------

src/test/regress/expected/float8-small-is-zero.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@ SELECT power(float8 'NaN', float8 'NaN');
362362
NaN
363363
(1 row)
364364

365+
SELECT power(float8 '-1', float8 'NaN');
366+
power
367+
-------
368+
NaN
369+
(1 row)
370+
365371
SELECT power(float8 '1', float8 'NaN');
366372
power
367373
-------

src/test/regress/expected/float8-small-is-zero_1.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@ SELECT power(float8 'NaN', float8 'NaN');
362362
NaN
363363
(1 row)
364364

365+
SELECT power(float8 '-1', float8 'NaN');
366+
power
367+
-------
368+
NaN
369+
(1 row)
370+
365371
SELECT power(float8 '1', float8 'NaN');
366372
power
367373
-------

src/test/regress/expected/float8.out

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ SELECT power(float8 'NaN', float8 'NaN');
358358
NaN
359359
(1 row)
360360

361+
SELECT power(float8 '-1', float8 'NaN');
362+
power
363+
-------
364+
NaN
365+
(1 row)
366+
361367
SELECT power(float8 '1', float8 'NaN');
362368
power
363369
-------

src/test/regress/sql/float8.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ SELECT power(float8 '144', float8 '0.5');
111111
SELECT power(float8 'NaN', float8 '0.5');
112112
SELECT power(float8 '144', float8 'NaN');
113113
SELECT power(float8 'NaN', float8 'NaN');
114+
SELECT power(float8 '-1', float8 'NaN');
114115
SELECT power(float8 '1', float8 'NaN');
115116
SELECT power(float8 'NaN', float8 '0');
116117

0 commit comments

Comments
 (0)