]> 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:44 +0000 (15:21 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 29 Apr 2018 19:21:44 +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 6522c0816ef01797e8a7fe33da50af29f1abe974..88215d910a548d53bddbf431b9e1fb2984db6ac5 100644 (file)
@@ -1566,14 +1566,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 7e1153308f55f4f374f1e80636ee16c195649b2b..3fe50b4bf008ee7478a9698a73be7f22b2cfb9c2 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 26b83781500c5b884096981a7bf74bdae30a6499..46adbe1c4ba9a35be65b47c72247486f11f18013 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 cea27908ebf1df102fa54d16d0212abc7c4a4d3f..f215eede103817684cb856c5a189aa362a5a3569 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 20c985e5df86ee6bcf603f45bec4df2947e1fad0..7159e78d4a7d440c28398b794a42f61b56772042 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 215e7a478499a1cebb91c19f571dfbdbbb7a29f8..5510fe9d3456cd171e9b24439b26c18322e718ad 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