diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2018-04-29 15:21:45 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2018-04-29 15:21:45 -0400 |
commit | 3b7fba935ec086c10ac407852ba840e358893e9c (patch) | |
tree | fa0f4aa3d86313c08dcd1a286913b73a1857df21 | |
parent | f47af9f2b231ae2e7bc9157b2bf676d9291439af (diff) | |
download | postgresql-3b7fba935ec086c10ac407852ba840e358893e9c.tar.gz postgresql-3b7fba935ec086c10ac407852ba840e358893e9c.zip |
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/75DB81BEEA95B445AE6D576A0A5C9E936A73E741@BPXM05GP.gisp.nec.co.jp
-rw-r--r-- | src/backend/utils/adt/float.c | 7 | ||||
-rw-r--r-- | src/test/regress/expected/float8-exp-three-digits-win32.out | 30 | ||||
-rw-r--r-- | src/test/regress/expected/float8-small-is-zero.out | 30 | ||||
-rw-r--r-- | src/test/regress/expected/float8-small-is-zero_1.out | 30 | ||||
-rw-r--r-- | src/test/regress/expected/float8.out | 30 | ||||
-rw-r--r-- | src/test/regress/sql/float8.sql | 5 |
6 files changed, 129 insertions, 3 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 76aa636213a..51390d15c6d 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -1409,14 +1409,15 @@ dpow(PG_FUNCTION_ARGS) * pow() sets errno only on some platforms, depending on whether it * follows _IEEE_, _POSIX_, _XOPEN_, or _SVID_, so we try to avoid using * errno. However, some platform/CPU combinations return errno == EDOM - * and result == Nan for negative arg1 and very large arg2 (they must be + * and result == NaN for negative arg1 and very large arg2 (they must be * using something different from our floor() test to decide it's * invalid). Other platforms (HPPA) return errno == ERANGE and a large - * (HUGE_VAL) but finite result to signal overflow. + * (HUGE_VAL) but finite result to signal overflow. Also, some versions + * of MSVC return errno == EDOM and result == NaN for NaN inputs. */ errno = 0; result = pow(arg1, arg2); - if (errno == EDOM && isnan(result)) + if (errno == EDOM && isnan(result) && !isnan(arg1) && !isnan(arg2)) { if ((fabs(arg1) > 1 && arg2 >= 0) || (fabs(arg1) < 1 && arg2 < 0)) /* The sign of Inf is not significant in this case. */ diff --git a/src/test/regress/expected/float8-exp-three-digits-win32.out b/src/test/regress/expected/float8-exp-three-digits-win32.out index 2dd648d6b92..8e4cbb05042 100644 --- a/src/test/regress/expected/float8-exp-three-digits-win32.out +++ b/src/test/regress/expected/float8-exp-three-digits-win32.out @@ -340,6 +340,36 @@ SELECT power(float8 '144', float8 '0.5'); 12 (1 row) +SELECT power(float8 'NaN', float8 '0.5'); + power +------- + NaN +(1 row) + +SELECT power(float8 '144', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 'NaN', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 '1', float8 'NaN'); + power +------- + 1 +(1 row) + +SELECT power(float8 'NaN', float8 '0'); + power +------- + 1 +(1 row) + -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f diff --git a/src/test/regress/expected/float8-small-is-zero.out b/src/test/regress/expected/float8-small-is-zero.out index 5da743374c9..e3e004451c1 100644 --- a/src/test/regress/expected/float8-small-is-zero.out +++ b/src/test/regress/expected/float8-small-is-zero.out @@ -344,6 +344,36 @@ SELECT power(float8 '144', float8 '0.5'); 12 (1 row) +SELECT power(float8 'NaN', float8 '0.5'); + power +------- + NaN +(1 row) + +SELECT power(float8 '144', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 'NaN', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 '1', float8 'NaN'); + power +------- + 1 +(1 row) + +SELECT power(float8 'NaN', float8 '0'); + power +------- + 1 +(1 row) + -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f diff --git a/src/test/regress/expected/float8-small-is-zero_1.out b/src/test/regress/expected/float8-small-is-zero_1.out index 530842e1023..e9487cffa64 100644 --- a/src/test/regress/expected/float8-small-is-zero_1.out +++ b/src/test/regress/expected/float8-small-is-zero_1.out @@ -344,6 +344,36 @@ SELECT power(float8 '144', float8 '0.5'); 12 (1 row) +SELECT power(float8 'NaN', float8 '0.5'); + power +------- + NaN +(1 row) + +SELECT power(float8 '144', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 'NaN', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 '1', float8 'NaN'); + power +------- + 1 +(1 row) + +SELECT power(float8 'NaN', float8 '0'); + power +------- + 1 +(1 row) + -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out index 6221538af5c..1bf225ca2b6 100644 --- a/src/test/regress/expected/float8.out +++ b/src/test/regress/expected/float8.out @@ -340,6 +340,36 @@ SELECT power(float8 '144', float8 '0.5'); 12 (1 row) +SELECT power(float8 'NaN', float8 '0.5'); + power +------- + NaN +(1 row) + +SELECT power(float8 '144', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 'NaN', float8 'NaN'); + power +------- + NaN +(1 row) + +SELECT power(float8 '1', float8 'NaN'); + power +------- + 1 +(1 row) + +SELECT power(float8 'NaN', float8 '0'); + power +------- + 1 +(1 row) + -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql index 92a574ab7bf..2b2e175c6d7 100644 --- a/src/test/regress/sql/float8.sql +++ b/src/test/regress/sql/float8.sql @@ -108,6 +108,11 @@ SELECT '' AS three, f.f1, |/f.f1 AS sqrt_f1 -- power SELECT power(float8 '144', float8 '0.5'); +SELECT power(float8 'NaN', float8 '0.5'); +SELECT power(float8 '144', float8 'NaN'); +SELECT power(float8 'NaN', float8 'NaN'); +SELECT power(float8 '1', float8 'NaN'); +SELECT power(float8 'NaN', float8 '0'); -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 |