diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 6a717f19bba..84d37de9304 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -1565,7 +1565,7 @@ dpow(PG_FUNCTION_ARGS) if (unlikely(isinf(result)) && !isinf(arg1) && !isinf(arg2)) float_overflow_error(); - if (unlikely(result == 0.0) && arg1 != 0.0) + if (unlikely(result == 0.0) && arg1 != 0.0 && !isinf(arg1) && !isinf(arg2)) float_underflow_error(); PG_RETURN_FLOAT8(result); @@ -1581,15 +1581,38 @@ dexp(PG_FUNCTION_ARGS) float8 arg1 = PG_GETARG_FLOAT8(0); float8 result; - errno = 0; - result = exp(arg1); - if (errno == ERANGE && result != 0 && !isinf(result)) - result = get_float8_infinity(); - - if (unlikely(isinf(result)) && !isinf(arg1)) - float_overflow_error(); - if (unlikely(result == 0.0)) - float_underflow_error(); + /* + * Handle NaN and Inf cases explicitly. This avoids needing to assume + * that the platform's exp() conforms to POSIX for these cases, and it + * removes some edge cases for the overflow checks below. + */ + if (isnan(arg1)) + result = arg1; + else if (isinf(arg1)) + { + /* Per POSIX, exp(-Inf) is 0 */ + result = (arg1 > 0.0) ? arg1 : 0; + } + else + { + /* + * On some platforms, exp() will not set errno but just return Inf or + * zero to report overflow/underflow; therefore, test both cases. + */ + errno = 0; + result = exp(arg1); + if (unlikely(errno == ERANGE)) + { + if (result != 0.0) + float_overflow_error(); + else + float_underflow_error(); + } + else if (unlikely(isinf(result))) + float_overflow_error(); + else if (unlikely(result == 0.0)) + float_underflow_error(); + } PG_RETURN_FLOAT8(result); } diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out index aaef20bcfdc..3957fb58d84 100644 --- a/src/test/regress/expected/float8.out +++ b/src/test/regress/expected/float8.out @@ -385,6 +385,158 @@ SELECT power(float8 'NaN', float8 '0'); 1 (1 row) +SELECT power(float8 'inf', float8 '0'); + power +------- + 1 +(1 row) + +SELECT power(float8 '-inf', float8 '0'); + power +------- + 1 +(1 row) + +SELECT power(float8 '0', float8 'inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '0', float8 '-inf'); +ERROR: zero raised to a negative power is undefined +SELECT power(float8 '1', float8 'inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '1', float8 '-inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '-1', float8 'inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '-1', float8 '-inf'); + power +------- + 1 +(1 row) + +SELECT power(float8 '0.1', float8 'inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-0.1', float8 'inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '1.1', float8 'inf'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 '-1.1', float8 'inf'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 '0.1', float8 '-inf'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 '-0.1', float8 '-inf'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 '1.1', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-1.1', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 'inf', float8 '-2'); + power +------- + 0 +(1 row) + +SELECT power(float8 'inf', float8 '2'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 'inf', float8 'inf'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 'inf', float8 '-inf'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-inf', float8 '-2'); + power +------- + 0 +(1 row) + +SELECT power(float8 '-inf', float8 '-3'); + power +------- + -0 +(1 row) + +SELECT power(float8 '-inf', float8 '2'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 '-inf', float8 '3'); + power +----------- + -Infinity +(1 row) + +SELECT power(float8 '-inf', float8 'inf'); + power +---------- + Infinity +(1 row) + +SELECT power(float8 '-inf', float8 '-inf'); + power +------- + 0 +(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 @@ -396,6 +548,13 @@ SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 | 1.2345678901234e-200 | 1.23456789012339e-200 (3 rows) +-- check edge cases for exp +SELECT exp('inf'::float8), exp('-inf'::float8), exp('nan'::float8); + exp | exp | exp +----------+-----+----- + Infinity | 0 | NaN +(1 row) + -- cube root SELECT ||/ float8 '27' AS three; three diff --git a/src/test/regress/sql/float8.sql b/src/test/regress/sql/float8.sql index e540f03b072..3a8c737fb28 100644 --- a/src/test/regress/sql/float8.sql +++ b/src/test/regress/sql/float8.sql @@ -120,12 +120,41 @@ SELECT power(float8 'NaN', float8 'NaN'); SELECT power(float8 '-1', float8 'NaN'); SELECT power(float8 '1', float8 'NaN'); SELECT power(float8 'NaN', float8 '0'); +SELECT power(float8 'inf', float8 '0'); +SELECT power(float8 '-inf', float8 '0'); +SELECT power(float8 '0', float8 'inf'); +SELECT power(float8 '0', float8 '-inf'); +SELECT power(float8 '1', float8 'inf'); +SELECT power(float8 '1', float8 '-inf'); +SELECT power(float8 '-1', float8 'inf'); +SELECT power(float8 '-1', float8 '-inf'); +SELECT power(float8 '0.1', float8 'inf'); +SELECT power(float8 '-0.1', float8 'inf'); +SELECT power(float8 '1.1', float8 'inf'); +SELECT power(float8 '-1.1', float8 'inf'); +SELECT power(float8 '0.1', float8 '-inf'); +SELECT power(float8 '-0.1', float8 '-inf'); +SELECT power(float8 '1.1', float8 '-inf'); +SELECT power(float8 '-1.1', float8 '-inf'); +SELECT power(float8 'inf', float8 '-2'); +SELECT power(float8 'inf', float8 '2'); +SELECT power(float8 'inf', float8 'inf'); +SELECT power(float8 'inf', float8 '-inf'); +SELECT power(float8 '-inf', float8 '-2'); +SELECT power(float8 '-inf', float8 '-3'); +SELECT power(float8 '-inf', float8 '2'); +SELECT power(float8 '-inf', float8 '3'); +SELECT power(float8 '-inf', float8 'inf'); +SELECT power(float8 '-inf', float8 '-inf'); -- take exp of ln(f.f1) SELECT '' AS three, f.f1, exp(ln(f.f1)) AS exp_ln_f1 FROM FLOAT8_TBL f WHERE f.f1 > '0.0'; +-- check edge cases for exp +SELECT exp('inf'::float8), exp('-inf'::float8), exp('nan'::float8); + -- cube root SELECT ||/ float8 '27' AS three;