aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2018-04-29 18:15:16 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2018-04-29 18:15:16 -0400
commit9e4caa2748f469d3f37a68f5012a2993dad0db10 (patch)
tree7da351d7a078af41e32930489041e1590dfcc594
parenteaed0d2305f294b8e6656930d1a22e22911d3ced (diff)
downloadpostgresql-9e4caa2748f469d3f37a68f5012a2993dad0db10.tar.gz
postgresql-9e4caa2748f469d3f37a68f5012a2993dad0db10.zip
Avoid wrong results for power() with NaN input on more platforms.
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
-rw-r--r--src/backend/utils/adt/float.c24
-rw-r--r--src/test/regress/expected/float8-exp-three-digits-win32.out6
-rw-r--r--src/test/regress/expected/float8-small-is-zero.out6
-rw-r--r--src/test/regress/expected/float8-small-is-zero_1.out6
-rw-r--r--src/test/regress/expected/float8.out6
-rw-r--r--src/test/regress/sql/float8.sql1
6 files changed, 46 insertions, 3 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index 51390d15c6d..4b57c1077df 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1392,6 +1392,25 @@ dpow(PG_FUNCTION_ARGS)
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
* divide-by-zero error code for 0 ^ -1.
@@ -1412,12 +1431,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. */
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 8e4cbb05042..1d0e3a46f2d 100644
--- a/src/test/regress/expected/float8-exp-three-digits-win32.out
+++ b/src/test/regress/expected/float8-exp-three-digits-win32.out
@@ -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
-------
diff --git a/src/test/regress/expected/float8-small-is-zero.out b/src/test/regress/expected/float8-small-is-zero.out
index e3e004451c1..fa47c9d2759 100644
--- a/src/test/regress/expected/float8-small-is-zero.out
+++ b/src/test/regress/expected/float8-small-is-zero.out
@@ -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
-------
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 e9487cffa64..15d48dfc399 100644
--- a/src/test/regress/expected/float8-small-is-zero_1.out
+++ b/src/test/regress/expected/float8-small-is-zero_1.out
@@ -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
-------
diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out
index 1bf225ca2b6..2d5f0247408 100644
--- a/src/test/regress/expected/float8.out
+++ b/src/test/regress/expected/float8.out
@@ -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
-------
diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql
index 2b2e175c6d7..78bec137e2f 100644
--- a/src/test/regress/sql/float8.sql
+++ b/src/test/regress/sql/float8.sql
@@ -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');