]> granicus.if.org Git - postgresql/commitdiff
Avoid wrong results for power() with NaN input on some platforms.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Apr 2018 19:21:45 +0000 (15:21 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Apr 2018 19:21:45 +0000 (15:21 -0400)
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

src/backend/utils/adt/float.c
src/test/regress/expected/float8-exp-three-digits-win32.out
src/test/regress/expected/float8-small-is-zero.out
src/test/regress/expected/float8-small-is-zero_1.out
src/test/regress/expected/float8.out
src/test/regress/sql/float8.sql

index 35aef890873402f1bcf20cab9c7fb0b6898dc7f9..3713632efdd4798876a78b04c86acbb1bb498655 100644 (file)
@@ -1473,14 +1473,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. */
index 2dd648d6b9221058cdfa1c2702b6d4a84d1c1ce5..8e4cbb05042b2d3cbf199612805874579014b008 100644 (file)
@@ -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
index 5da743374c9174112fd7bfcaff2b17f25bd1d3a3..e3e004451c1cdbcef011a29a5ce5c63e358a203a 100644 (file)
@@ -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
index 530842e10233f096b22f371d2dd597e0fd376743..e9487cffa6430ba5d0ed99da61eda26bbf65ff29 100644 (file)
@@ -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
index 6221538af5c9b8f8adcbd3755eda467e94cc254d..1bf225ca2b67542c3bc218e31c8eafceb4a2eeeb 100644 (file)
@@ -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
index 92a574ab7bff24aa688d265cee98b4631cc19bc5..2b2e175c6d7a489f4fecd46bbed6f26298722245 100644 (file)
@@ -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