postgres.git / summary / log / commit / refs

commit    d1fc750b5199837a330c10d542191c8ab03a7dc8
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Thu May 17 15:10:50 2018 +0000
First released in: 11.0

    Make numeric power() handle NaNs according to the modern POSIX spec.
    
    In commit 6bdf1303b, we ensured that power()/^ for float8 would honor
    the NaN behaviors specified by POSIX standards released in this century,
    ie NaN ^ 0 = 1 and 1 ^ NaN = 1.  However, numeric_power() was not
    touched and continued to follow the once-common behavior that every
    case involving NaN input produces NaN.  For consistency, let's switch
    the numeric behavior to the modern spec in the same release that ensures
    that behavior for float8.
    
    (Note that while 6bdf1303b was initially back-patched, we later undid
    that, concluding that any behavioral change should appear only in v11.)
    
    Discussion: https://postgr.es/m/10898.1526421338@sss.pgh.pa.us


src/backend/utils/adt/numeric.c | 21 +++++++++++++++++++-- src/test/regress/expected/numeric.out | 31 +++++++++++++++++++++++++++++++ src/test/regress/sql/numeric.sql | 7 +++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index dcf31e340c1..8dfdffcfbd1 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -2972,10 +2972,27 @@ numeric_power(PG_FUNCTION_ARGS) NumericVar result; /* - * Handle NaN + * Handle NaN cases. We follow the POSIX spec for pow(3), which says that + * NaN ^ 0 = 1, and 1 ^ NaN = 1, while all other cases with NaN inputs + * yield NaN (with no error). */ - if (NUMERIC_IS_NAN(num1) || NUMERIC_IS_NAN(num2)) + if (NUMERIC_IS_NAN(num1)) + { + if (!NUMERIC_IS_NAN(num2)) + { + init_var_from_num(num2, &arg2); + if (cmp_var(&arg2, &const_zero) == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + } PG_RETURN_NUMERIC(make_result(&const_nan)); + } + if (NUMERIC_IS_NAN(num2)) + { + init_var_from_num(num1, &arg1); + if (cmp_var(&arg1, &const_one) == 0) + PG_RETURN_NUMERIC(make_result(&const_one)); + PG_RETURN_NUMERIC(make_result(&const_nan)); + } /* * Initialize things diff --git a/src/test/regress/expected/numeric.out b/src/test/regress/expected/numeric.out index 17985e85401..1cb3c3bfab7 100644 --- a/src/test/regress/expected/numeric.out +++ b/src/test/regress/expected/numeric.out @@ -1664,6 +1664,37 @@ select 0.0 ^ 12.34; 0.0000000000000000 (1 row) +-- NaNs +select 'NaN'::numeric ^ 'NaN'::numeric; + ?column? +---------- + NaN +(1 row) + +select 'NaN'::numeric ^ 0; + ?column? +---------- + 1 +(1 row) + +select 'NaN'::numeric ^ 1; + ?column? +---------- + NaN +(1 row) + +select 0 ^ 'NaN'::numeric; + ?column? +---------- + NaN +(1 row) + +select 1 ^ 'NaN'::numeric; + ?column? +---------- + 1 +(1 row) + -- invalid inputs select 0.0 ^ (-12.34); ERROR: zero raised to a negative power is undefined diff --git a/src/test/regress/sql/numeric.sql b/src/test/regress/sql/numeric.sql index d77504e6246..a9394123596 100644 --- a/src/test/regress/sql/numeric.sql +++ b/src/test/regress/sql/numeric.sql @@ -911,6 +911,13 @@ select (-12.34) ^ 0.0; select 12.34 ^ 0.0; select 0.0 ^ 12.34; +-- NaNs +select 'NaN'::numeric ^ 'NaN'::numeric; +select 'NaN'::numeric ^ 0; +select 'NaN'::numeric ^ 1; +select 0 ^ 'NaN'::numeric; +select 1 ^ 'NaN'::numeric; + -- invalid inputs select 0.0 ^ (-12.34); select (-12.34) ^ 1.2; [parent: b2b82228ee5d]