diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2020-06-15 19:10:30 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2020-06-15 19:10:33 -0400 |
commit | 5674eb98762ed41c7f9672a3bc56edf925e9d97d (patch) | |
tree | 0b630f2b3413d3fabe456a655d4bab535a3c0f65 /src/backend/utils/adt/float.c | |
parent | 2961c9711c17e5fe05fb1fbd72c3e47f3b601ee4 (diff) | |
download | postgresql-5674eb98762ed41c7f9672a3bc56edf925e9d97d.tar.gz postgresql-5674eb98762ed41c7f9672a3bc56edf925e9d97d.zip |
Fix power() for large inputs yet more.
Buildfarm results for commit e532b1d57 reveal the error in my thinking
about the unexpected-EDOM case. I'd supposed this was no longer really
a live issue, but it seems the fix for glibc's bug #3866 is not all that
old, and we still have at least one buildfarm animal (lapwing) with the
bug. Hence, resurrect essentially the previous logic (but, I hope, less
opaquely presented), and explain what it is we're really doing here.
Also, blindly try to fix fossa's failure by tweaking the logic that
figures out whether y is an odd integer when x is -inf. This smells
a whole lot like a compiler bug, but I lack access to icc to try to
pin it down. Maybe doing division instead of multiplication will
dodge the issue.
Discussion: https://postgr.es/m/E1jkU7H-00024V-NZ@gemulon.postgresql.org
Diffstat (limited to 'src/backend/utils/adt/float.c')
-rw-r--r-- | src/backend/utils/adt/float.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 08ebbf4678e..086631dd19f 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -1583,7 +1583,7 @@ dpow(PG_FUNCTION_ARGS) if (arg2 == floor(arg2)) { /* y is integral; it's odd if y/2 is not integral */ - double halfy = arg2 * 0.5; /* should be computed exactly */ + double halfy = arg2 / 2; /* should be computed exactly */ if (halfy != floor(halfy)) yisoddinteger = true; @@ -1608,17 +1608,29 @@ dpow(PG_FUNCTION_ARGS) if (errno == EDOM || isnan(result)) { /* - * We eliminated all the possible domain errors above, or should - * have; but if pow() has a more restrictive test for "is y an - * integer?" than we do, we could get here anyway. Historical - * evidence suggests that some platforms once implemented the test - * as "y == (long) y", which of course misbehaves beyond LONG_MAX. - * There's not a lot of choice except to accept the platform's - * conclusion that we have a domain error. + * We handled all possible domain errors above, so this should be + * impossible. However, old glibc versions on x86 have a bug that + * causes them to fail this way for abs(y) greater than 2^63: + * + * https://sourceware.org/bugzilla/show_bug.cgi?id=3866 + * + * Hence, if we get here, assume y is finite but large (large + * enough to be certainly even). The result should be 0 if x == 0, + * 1.0 if abs(x) == 1.0, otherwise an overflow or underflow error. */ - ereport(ERROR, - (errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION), - errmsg("a negative number raised to a non-integer power yields a complex result"))); + if (arg1 == 0.0) + result = 0.0; /* we already verified y is positive */ + else + { + double absx = fabs(arg1); + + if (absx == 1.0) + result = 1.0; + else if (arg2 >= 0.0 ? (absx > 1.0) : (absx < 1.0)) + float_overflow_error(); + else + float_underflow_error(); + } } else if (errno == ERANGE) { |