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
This commit is contained in:
Tom Lane 2018-04-29 18:15:16 -04:00
parent 68e7e973d2
commit 6bdf1303b3
6 changed files with 46 additions and 3 deletions

View File

@ -1548,6 +1548,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
@ -1569,12 +1588,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. */

View 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
-------

View 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
-------

View 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
-------

View 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
-------

View 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');