Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e532b1d

Browse files
committed
Fix power() for infinity inputs some more.
Buildfarm results for commit decbe2b show that AIX and illumos have non-POSIX-compliant pow() functions, as do ancient NetBSD and HPUX releases. While it's dubious how much we should care about the latter two platforms, the former two are probably enough reason to put in manual handling of infinite-input cases. Hence, do so, and clean up the post-pow() error handling to reflect its now-more-limited scope. (Notably, while we no longer expect to ever see EDOM from pow(), report it as a domain error if we do. The former coding had the net effect of expensively converting the error to ERANGE, which seems highly questionable: if pow() wanted to report ERANGE, it would have done so.) Patch by me; thanks to Michael Paquier for review. Discussion: https://postgr.es/m/E1jkU7H-00024V-NZ@gemulon.postgresql.org
1 parent 7a3543c commit e532b1d

File tree

3 files changed

+94
-23
lines changed

3 files changed

+94
-23
lines changed

src/backend/utils/adt/float.c

Lines changed: 91 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,33 +1540,101 @@ dpow(PG_FUNCTION_ARGS)
15401540
errmsg("a negative number raised to a non-integer power yields a complex result")));
15411541

15421542
/*
1543-
* pow() sets errno only on some platforms, depending on whether it
1544-
* follows _IEEE_, _POSIX_, _XOPEN_, or _SVID_, so we try to avoid using
1545-
* errno. However, some platform/CPU combinations return errno == EDOM
1546-
* and result == NaN for negative arg1 and very large arg2 (they must be
1547-
* using something different from our floor() test to decide it's
1548-
* invalid). Other platforms (HPPA) return errno == ERANGE and a large
1549-
* (HUGE_VAL) but finite result to signal overflow.
1543+
* We don't trust the platform's pow() to handle infinity cases per POSIX
1544+
* spec either, so deal with those explicitly too. It's easier to handle
1545+
* infinite y first, so that it doesn't matter if x is also infinite.
15501546
*/
1551-
errno = 0;
1552-
result = pow(arg1, arg2);
1553-
if (errno == EDOM && isnan(result))
1547+
if (isinf(arg2))
15541548
{
1555-
if ((fabs(arg1) > 1 && arg2 >= 0) || (fabs(arg1) < 1 && arg2 < 0))
1556-
/* The sign of Inf is not significant in this case. */
1557-
result = get_float8_infinity();
1558-
else if (fabs(arg1) != 1)
1559-
result = 0;
1560-
else
1561-
result = 1;
1549+
double absx = fabs(arg1);
1550+
1551+
if (absx == 1.0)
1552+
result = 1.0;
1553+
else if (arg2 > 0.0) /* y = +Inf */
1554+
{
1555+
if (absx > 1.0)
1556+
result = arg2;
1557+
else
1558+
result = 0.0;
1559+
}
1560+
else /* y = -Inf */
1561+
{
1562+
if (absx > 1.0)
1563+
result = 0.0;
1564+
else
1565+
result = -arg2;
1566+
}
15621567
}
1563-
else if (errno == ERANGE && result != 0 && !isinf(result))
1564-
result = get_float8_infinity();
1568+
else if (isinf(arg1))
1569+
{
1570+
if (arg2 == 0.0)
1571+
result = 1.0;
1572+
else if (arg1 > 0.0) /* x = +Inf */
1573+
{
1574+
if (arg2 > 0.0)
1575+
result = arg1;
1576+
else
1577+
result = 0.0;
1578+
}
1579+
else /* x = -Inf */
1580+
{
1581+
bool yisoddinteger = false;
15651582

1566-
if (unlikely(isinf(result)) && !isinf(arg1) && !isinf(arg2))
1567-
float_overflow_error();
1568-
if (unlikely(result == 0.0) && arg1 != 0.0 && !isinf(arg1) && !isinf(arg2))
1569-
float_underflow_error();
1583+
if (arg2 == floor(arg2))
1584+
{
1585+
/* y is integral; it's odd if y/2 is not integral */
1586+
double halfy = arg2 * 0.5; /* should be computed exactly */
1587+
1588+
if (halfy != floor(halfy))
1589+
yisoddinteger = true;
1590+
}
1591+
if (arg2 > 0.0)
1592+
result = yisoddinteger ? arg1 : -arg1;
1593+
else
1594+
result = yisoddinteger ? -0.0 : 0.0;
1595+
}
1596+
}
1597+
else
1598+
{
1599+
/*
1600+
* pow() sets errno on only some platforms, depending on whether it
1601+
* follows _IEEE_, _POSIX_, _XOPEN_, or _SVID_, so we must check both
1602+
* errno and invalid output values. (We can't rely on just the
1603+
* latter, either; some old platforms return a large-but-finite
1604+
* HUGE_VAL when reporting overflow.)
1605+
*/
1606+
errno = 0;
1607+
result = pow(arg1, arg2);
1608+
if (errno == EDOM || isnan(result))
1609+
{
1610+
/*
1611+
* We eliminated all the possible domain errors above, or should
1612+
* have; but if pow() has a more restrictive test for "is y an
1613+
* integer?" than we do, we could get here anyway. Historical
1614+
* evidence suggests that some platforms once implemented the test
1615+
* as "y == (long) y", which of course misbehaves beyond LONG_MAX.
1616+
* There's not a lot of choice except to accept the platform's
1617+
* conclusion that we have a domain error.
1618+
*/
1619+
ereport(ERROR,
1620+
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
1621+
errmsg("a negative number raised to a non-integer power yields a complex result")));
1622+
}
1623+
else if (errno == ERANGE)
1624+
{
1625+
if (result != 0.0)
1626+
float_overflow_error();
1627+
else
1628+
float_underflow_error();
1629+
}
1630+
else
1631+
{
1632+
if (unlikely(isinf(result)))
1633+
float_overflow_error();
1634+
if (unlikely(result == 0.0) && arg1 != 0.0)
1635+
float_underflow_error();
1636+
}
1637+
}
15701638

15711639
PG_RETURN_FLOAT8(result);
15721640
}

src/test/regress/expected/float8.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,8 @@ SELECT power(float8 '-inf', float8 '3');
525525
-Infinity
526526
(1 row)
527527

528+
SELECT power(float8 '-inf', float8 '3.5');
529+
ERROR: a negative number raised to a non-integer power yields a complex result
528530
SELECT power(float8 '-inf', float8 'inf');
529531
power
530532
----------

src/test/regress/sql/float8.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ SELECT power(float8 '-inf', float8 '-2');
144144
SELECT power(float8 '-inf', float8 '-3');
145145
SELECT power(float8 '-inf', float8 '2');
146146
SELECT power(float8 '-inf', float8 '3');
147+
SELECT power(float8 '-inf', float8 '3.5');
147148
SELECT power(float8 '-inf', float8 'inf');
148149
SELECT power(float8 '-inf', float8 '-inf');
149150

0 commit comments

Comments
 (0)