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

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 3713632efdd4798876a78b04c86acbb1bb498655..44d3ba0c93c9e486e218dc0f048a9f9d45d93d02 100644 (file)
@@ -1455,6 +1455,25 @@ dpow(PG_FUNCTION_ARGS)
        float8          arg2 = PG_GETARG_FLOAT8(1);
        float8          result;
 
+       /*
+        * The POSIX spec says that NaN ^ 0 = 1, and 1 ^ NaN = 1, while all other
+        * cases with NaN inputs yield NaN (with no error).  Many older platforms
+        * get one or more of these cases wrong, so deal with them via explicit
+        * logic rather than trusting pow(3).
+        */
+       if (isnan(arg1))
+       {
+               if (isnan(arg2) || arg2 != 0.0)
+                       PG_RETURN_FLOAT8(get_float8_nan());
+               PG_RETURN_FLOAT8(1.0);
+       }
+       if (isnan(arg2))
+       {
+               if (arg1 != 1.0)
+                       PG_RETURN_FLOAT8(get_float8_nan());
+               PG_RETURN_FLOAT8(1.0);
+       }
+
        /*
         * The SQL spec requires that we emit a particular SQLSTATE error code for
         * certain error conditions.  Specifically, we don't return a
@@ -1476,12 +1495,11 @@ dpow(PG_FUNCTION_ARGS)
         * 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.  Also, some versions
-        * of MSVC return errno == EDOM and result == NaN for NaN inputs.
+        * (HUGE_VAL) but finite result to signal overflow.
         */
        errno = 0;
        result = pow(arg1, arg2);
-       if (errno == EDOM && isnan(result) && !isnan(arg1) && !isnan(arg2))
+       if (errno == EDOM && isnan(result))
        {
                if ((fabs(arg1) > 1 && arg2 >= 0) || (fabs(arg1) < 1 && arg2 < 0))
                        /* The sign of Inf is not significant in this case. */
index 8e4cbb05042b2d3cbf199612805874579014b008..1d0e3a46f2d89bfd269c7927b2957a9822053c62 100644 (file)
@@ -358,6 +358,12 @@ SELECT power(float8 'NaN', float8 'NaN');
    NaN
 (1 row)
 
+SELECT power(float8 '-1', float8 'NaN');
+ power 
+-------
+   NaN
+(1 row)
+
 SELECT power(float8 '1', float8 'NaN');
  power 
 -------
index e3e004451c1cdbcef011a29a5ce5c63e358a203a..fa47c9d27593f3fe70989b53c7df583e556192d3 100644 (file)
@@ -362,6 +362,12 @@ SELECT power(float8 'NaN', float8 'NaN');
    NaN
 (1 row)
 
+SELECT power(float8 '-1', float8 'NaN');
+ power 
+-------
+   NaN
+(1 row)
+
 SELECT power(float8 '1', float8 'NaN');
  power 
 -------
index e9487cffa6430ba5d0ed99da61eda26bbf65ff29..15d48dfc399c74bb3e6402395dede08a69b05749 100644 (file)
@@ -362,6 +362,12 @@ SELECT power(float8 'NaN', float8 'NaN');
    NaN
 (1 row)
 
+SELECT power(float8 '-1', float8 'NaN');
+ power 
+-------
+   NaN
+(1 row)
+
 SELECT power(float8 '1', float8 'NaN');
  power 
 -------
index 1bf225ca2b67542c3bc218e31c8eafceb4a2eeeb..2d5f02474084a4f63d879bddc6a61f6c8c42175d 100644 (file)
@@ -358,6 +358,12 @@ SELECT power(float8 'NaN', float8 'NaN');
    NaN
 (1 row)
 
+SELECT power(float8 '-1', float8 'NaN');
+ power 
+-------
+   NaN
+(1 row)
+
 SELECT power(float8 '1', float8 'NaN');
  power 
 -------
index 2b2e175c6d7a489f4fecd46bbed6f26298722245..78bec137e2f8bbab6c80f21920a9512e8cb0bff7 100644 (file)
@@ -111,6 +111,7 @@ 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 '1', float8 'NaN');
 SELECT power(float8 'NaN', float8 '0');