Skip to content

Commit 61b200e

Browse files
committed
Avoid wrong results for power() with NaN input on some platforms.
Per spec, the result of power() should be NaN if either input is NaN. It appears that on some versions of Windows, the libc function does return NaN, but it also sets errno = EDOM, confusing our code that attempts to work around shortcomings of other platforms. Hence, add guard tests to avoid substituting a wrong result for the right one. It's been like this for a long time (and the odd behavior only appears in older MSVC releases, too) so back-patch to all supported branches. Dang Minh Huong, reviewed by David Rowley Discussion: https://postgr.es/m/[email protected]
1 parent 85475af commit 61b200e

File tree

6 files changed

+129
-3
lines changed

6 files changed

+129
-3
lines changed

src/backend/utils/adt/float.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -1566,14 +1566,15 @@ dpow(PG_FUNCTION_ARGS)
15661566
* pow() sets errno only on some platforms, depending on whether it
15671567
* follows _IEEE_, _POSIX_, _XOPEN_, or _SVID_, so we try to avoid using
15681568
* errno. However, some platform/CPU combinations return errno == EDOM
1569-
* and result == Nan for negative arg1 and very large arg2 (they must be
1569+
* and result == NaN for negative arg1 and very large arg2 (they must be
15701570
* using something different from our floor() test to decide it's
15711571
* invalid). Other platforms (HPPA) return errno == ERANGE and a large
1572-
* (HUGE_VAL) but finite result to signal overflow.
1572+
* (HUGE_VAL) but finite result to signal overflow. Also, some versions
1573+
* of MSVC return errno == EDOM and result == NaN for NaN inputs.
15731574
*/
15741575
errno = 0;
15751576
result = pow(arg1, arg2);
1576-
if (errno == EDOM && isnan(result))
1577+
if (errno == EDOM && isnan(result) && !isnan(arg1) && !isnan(arg2))
15771578
{
15781579
if ((fabs(arg1) > 1 && arg2 >= 0) || (fabs(arg1) < 1 && arg2 < 0))
15791580
/* The sign of Inf is not significant in this case. */

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

+30
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,36 @@ SELECT power(float8 '144', float8 '0.5');
340340
12
341341
(1 row)
342342

343+
SELECT power(float8 'NaN', float8 '0.5');
344+
power
345+
-------
346+
NaN
347+
(1 row)
348+
349+
SELECT power(float8 '144', float8 'NaN');
350+
power
351+
-------
352+
NaN
353+
(1 row)
354+
355+
SELECT power(float8 'NaN', float8 'NaN');
356+
power
357+
-------
358+
NaN
359+
(1 row)
360+
361+
SELECT power(float8 '1', float8 'NaN');
362+
power
363+
-------
364+
1
365+
(1 row)
366+
367+
SELECT power(float8 'NaN', float8 '0');
368+
power
369+
-------
370+
1
371+
(1 row)
372+
343373
-- take exp of ln(f.f1)
344374
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
345375
FROM FLOAT8_TBL f

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

+30
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,36 @@ SELECT power(float8 '144', float8 '0.5');
344344
12
345345
(1 row)
346346

347+
SELECT power(float8 'NaN', float8 '0.5');
348+
power
349+
-------
350+
NaN
351+
(1 row)
352+
353+
SELECT power(float8 '144', float8 'NaN');
354+
power
355+
-------
356+
NaN
357+
(1 row)
358+
359+
SELECT power(float8 'NaN', float8 'NaN');
360+
power
361+
-------
362+
NaN
363+
(1 row)
364+
365+
SELECT power(float8 '1', float8 'NaN');
366+
power
367+
-------
368+
1
369+
(1 row)
370+
371+
SELECT power(float8 'NaN', float8 '0');
372+
power
373+
-------
374+
1
375+
(1 row)
376+
347377
-- take exp of ln(f.f1)
348378
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
349379
FROM FLOAT8_TBL f

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

+30
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,36 @@ SELECT power(float8 '144', float8 '0.5');
344344
12
345345
(1 row)
346346

347+
SELECT power(float8 'NaN', float8 '0.5');
348+
power
349+
-------
350+
NaN
351+
(1 row)
352+
353+
SELECT power(float8 '144', float8 'NaN');
354+
power
355+
-------
356+
NaN
357+
(1 row)
358+
359+
SELECT power(float8 'NaN', float8 'NaN');
360+
power
361+
-------
362+
NaN
363+
(1 row)
364+
365+
SELECT power(float8 '1', float8 'NaN');
366+
power
367+
-------
368+
1
369+
(1 row)
370+
371+
SELECT power(float8 'NaN', float8 '0');
372+
power
373+
-------
374+
1
375+
(1 row)
376+
347377
-- take exp of ln(f.f1)
348378
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
349379
FROM FLOAT8_TBL f

src/test/regress/expected/float8.out

+30
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,36 @@ SELECT power(float8 '144', float8 '0.5');
340340
12
341341
(1 row)
342342

343+
SELECT power(float8 'NaN', float8 '0.5');
344+
power
345+
-------
346+
NaN
347+
(1 row)
348+
349+
SELECT power(float8 '144', float8 'NaN');
350+
power
351+
-------
352+
NaN
353+
(1 row)
354+
355+
SELECT power(float8 'NaN', float8 'NaN');
356+
power
357+
-------
358+
NaN
359+
(1 row)
360+
361+
SELECT power(float8 '1', float8 'NaN');
362+
power
363+
-------
364+
1
365+
(1 row)
366+
367+
SELECT power(float8 'NaN', float8 '0');
368+
power
369+
-------
370+
1
371+
(1 row)
372+
343373
-- take exp of ln(f.f1)
344374
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1
345375
FROM FLOAT8_TBL f

src/test/regress/sql/float8.sql

+5
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ SELECT '' AS three, f.f1, |/f.f1 AS sqrt_f1
108108

109109
-- power
110110
SELECT power(float8 '144', float8 '0.5');
111+
SELECT power(float8 'NaN', float8 '0.5');
112+
SELECT power(float8 '144', float8 'NaN');
113+
SELECT power(float8 'NaN', float8 'NaN');
114+
SELECT power(float8 '1', float8 'NaN');
115+
SELECT power(float8 'NaN', float8 '0');
111116

112117
-- take exp of ln(f.f1)
113118
SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1

0 commit comments

Comments
 (0)