Fix power() for large inputs yet more.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 15 Jun 2020 23:10:30 +0000 (19:10 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 15 Jun 2020 23:10:33 +0000 (19:10 -0400)
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

src/backend/utils/adt/float.c

index 08ebbf4678ea55ef70d0726082506f15c95b52ad..086631dd19ff4e345c44b8925e7184cb12569f54 100644 (file)
@@ -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)
                {