Moin,
On 2020-02-07 15:42, Emre Hasegeli wrote:
> The patch looks unduly invasive to me, but I think that it might be
> right that we should go back to a macro-based implementation, because
> otherwise we don't have a good way to be certain that the function
> parameter won't get evaluated first.
I'd first like to see some actual evidence of this being a problem,
rather than just the order of the checks.
There seem to be enough evidence of this being the problem. We are
better off going back to the macro-based implementation. I polished
Keisuke Kuroda's patch commenting about the performance issue, removed
the check_float*_val() functions completely, and added unlikely() as
Tom Lane suggested. It is attached. I confirmed with different
compilers that the macro, and unlikely() makes this noticeably faster.
Hm, the diff has the macro tests as:
+ if (unlikely(isinf(val) && !(inf_is_valid)))
...
+ if (unlikely((val) == 0.0 && !(zero_is_valid)))
But the comment does not explain that this test has to be in that
order, or the compiler will for non-constant arguments evalute
the (now) right-side first. E.g. if I understand this correctly:
+ if (!(zero_is_valid) && unlikely((val) == 0.0)
would have the same problem of evaluating "zero_is_valid" (which
might be an isinf(arg1) || isinf(arg2)) first and so be the same thing
we try to avoid with the macro? Maybe adding this bit of info to the
comment makes it clearer?
Also, a few places use the macro as:
+ CHECKFLOATVAL(result, true, true);
which evaluates to a complete NOP in both cases. IMHO this could be
replaced with a comment like:
+ // No CHECKFLOATVAL() needed, as both inf and 0.0 are valid
(or something along the lines of "no error can occur"), as otherwise
CHECKFLOATVAL() implies to the casual reader that there are some checks
done, while in reality no real checks are done at all (and hopefully
the compiler optimizes everything away, which might not be true for
debug builds).
--
Best regards,
Tels
From e869373ad093e668872f08833de2c5c614aab673 Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <e...@hasegeli.com>
Date: Fri, 7 Feb 2020 10:27:25 +0000
Subject: [PATCH] Bring back CHECKFLOATVAL() macro
The inline functions added by 6bf0bc842b caused the conditions of
overflow/underflow checks to be evaluated when no overflow/underflow
happen. This slowed down floating point operations. This commit brings
back the macro that was in use before 6bf0bc842b to fix the performace
regression.
Reported-by: Keisuke Kuroda <keisuke.kuroda.3...@gmail.com>
Author: Keisuke Kuroda <keisuke.kuroda.3...@gmail.com>
Discussion: https://www.postgresql.org/message-id/CANDwggLe1Gc1OrRqvPfGE%3DkM9K0FSfia0hbeFCEmwabhLz95AA%40mail.gmail.com
---
src/backend/utils/adt/float.c | 66 ++++++++++++++---------------
src/backend/utils/adt/geo_ops.c | 2 +-
src/include/utils/float.h | 75 ++++++++++++++-------------------
3 files changed, 66 insertions(+), 77 deletions(-)
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index a90d4db215..5885719850 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -1184,21 +1184,21 @@ ftod(PG_FUNCTION_ARGS)
/*
* dtof - converts a float8 number to a float4 number
*/
Datum
dtof(PG_FUNCTION_ARGS)
{
float8 num = PG_GETARG_FLOAT8(0);
- check_float4_val((float4) num, isinf(num), num == 0);
+ CHECKFLOATVAL((float4) num, isinf(num), num == 0);
PG_RETURN_FLOAT4((float4) num);
}
/*
* dtoi4 - converts a float8 number to an int4 number
*/
Datum
dtoi4(PG_FUNCTION_ARGS)
@@ -1438,36 +1438,36 @@ dsqrt(PG_FUNCTION_ARGS)
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_POWER_FUNCTION),
errmsg("cannot take square root of a negative number")));
result = sqrt(arg1);
- check_float8_val(result, isinf(arg1), arg1 == 0);
+ CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dcbrt - returns cube root of arg1
*/
Datum
dcbrt(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
result = cbrt(arg1);
- check_float8_val(result, isinf(arg1), arg1 == 0);
+ CHECKFLOATVAL(result, isinf(arg1), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dpow - returns pow(arg1,arg2)
*/
Datum
dpow(PG_FUNCTION_ARGS)
{
@@ -1525,40 +1525,40 @@ dpow(PG_FUNCTION_ARGS)
/* The sign of Inf is not significant in this case. */
result = get_float8_infinity();
else if (fabs(arg1) != 1)
result = 0;
else
result = 1;
}
else if (errno == ERANGE && result != 0 && !isinf(result))
result = get_float8_infinity();
- check_float8_val(result, isinf(arg1) || isinf(arg2), arg1 == 0);
+ CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
}
/*
* dexp - returns the exponential function of arg1
*/
Datum
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();
- check_float8_val(result, isinf(arg1), false);
+ CHECKFLOATVAL(result, isinf(arg1), false);
PG_RETURN_FLOAT8(result);
}
/*
* dlog1 - returns the natural logarithm of arg1
*/
Datum
dlog1(PG_FUNCTION_ARGS)
{
@@ -1573,21 +1573,21 @@ dlog1(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of zero")));
if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of a negative number")));
result = log(arg1);
- check_float8_val(result, isinf(arg1), arg1 == 1);
+ CHECKFLOATVAL(result, isinf(arg1), arg1 == 1);
PG_RETURN_FLOAT8(result);
}
/*
* dlog10 - returns the base 10 logarithm of arg1
*/
Datum
dlog10(PG_FUNCTION_ARGS)
{
@@ -1603,21 +1603,21 @@ dlog10(PG_FUNCTION_ARGS)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of zero")));
if (arg1 < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_LOG),
errmsg("cannot take logarithm of a negative number")));
result = log10(arg1);
- check_float8_val(result, isinf(arg1), arg1 == 1);
+ CHECKFLOATVAL(result, isinf(arg1), arg1 == 1);
PG_RETURN_FLOAT8(result);
}
/*
* dacos - returns the arccos of arg1 (radians)
*/
Datum
dacos(PG_FUNCTION_ARGS)
{
@@ -1633,21 +1633,21 @@ dacos(PG_FUNCTION_ARGS)
* range [-1, 1] to values in the range [0, Pi], so we should reject any
* inputs outside that range and the result will always be finite.
*/
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = acos(arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dasin - returns the arcsin of arg1 (radians)
*/
Datum
dasin(PG_FUNCTION_ARGS)
{
@@ -1663,21 +1663,21 @@ dasin(PG_FUNCTION_ARGS)
* range [-1, 1] to values in the range [-Pi/2, Pi/2], so we should reject
* any inputs outside that range and the result will always be finite.
*/
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = asin(arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* datan - returns the arctan of arg1 (radians)
*/
Datum
datan(PG_FUNCTION_ARGS)
{
@@ -1688,21 +1688,21 @@ datan(PG_FUNCTION_ARGS)
if (isnan(arg1))
PG_RETURN_FLOAT8(get_float8_nan());
/*
* The principal branch of the inverse tangent function maps all inputs to
* values in the range [-Pi/2, Pi/2], so the result should always be
* finite, even if the input is infinite.
*/
result = atan(arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* atan2 - returns the arctan of arg1/arg2 (radians)
*/
Datum
datan2(PG_FUNCTION_ARGS)
{
@@ -1713,21 +1713,21 @@ datan2(PG_FUNCTION_ARGS)
/* Per the POSIX spec, return NaN if either input is NaN */
if (isnan(arg1) || isnan(arg2))
PG_RETURN_FLOAT8(get_float8_nan());
/*
* atan2 maps all inputs to values in the range [-Pi, Pi], so the result
* should always be finite, even if the inputs are infinite.
*/
result = atan2(arg1, arg2);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcos - returns the cosine of arg1 (radians)
*/
Datum
dcos(PG_FUNCTION_ARGS)
{
@@ -1753,21 +1753,21 @@ dcos(PG_FUNCTION_ARGS)
* platform reports via errno, so also explicitly test for infinite
* inputs.
*/
errno = 0;
result = cos(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcot - returns the cotangent of arg1 (radians)
*/
Datum
dcot(PG_FUNCTION_ARGS)
{
@@ -1780,21 +1780,21 @@ dcot(PG_FUNCTION_ARGS)
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = tan(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = 1.0 / result;
- check_float8_val(result, true /* cot(0) == Inf */ , true);
+ CHECKFLOATVAL(result, true /* cot(0) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/*
* dsin - returns the sine of arg1 (radians)
*/
Datum
dsin(PG_FUNCTION_ARGS)
{
@@ -1806,21 +1806,21 @@ dsin(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = sin(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dtan - returns the tangent of arg1 (radians)
*/
Datum
dtan(PG_FUNCTION_ARGS)
{
@@ -1832,21 +1832,21 @@ dtan(PG_FUNCTION_ARGS)
PG_RETURN_FLOAT8(get_float8_nan());
/* Be sure to throw an error if the input is infinite --- see dcos() */
errno = 0;
result = tan(arg1);
if (errno != 0 || isinf(arg1))
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
- check_float8_val(result, true /* tan(pi/2) == Inf */ , true);
+ CHECKFLOATVAL(result, true /* tan(pi/2) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/* ========== DEGREE-BASED TRIGONOMETRIC FUNCTIONS ========== */
/*
* Initialize the cached constants declared at the head of this file
* (sin_30 etc). The fact that we need those at all, let alone need this
@@ -1984,21 +1984,21 @@ dacosd(PG_FUNCTION_ARGS)
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
if (arg1 >= 0.0)
result = acosd_q1(arg1);
else
result = 90.0 + asind_q1(-arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dasind - returns the arcsin of arg1 (degrees)
*/
Datum
dasind(PG_FUNCTION_ARGS)
{
@@ -2019,21 +2019,21 @@ dasind(PG_FUNCTION_ARGS)
if (arg1 < -1.0 || arg1 > 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
if (arg1 >= 0.0)
result = asind_q1(arg1);
else
result = -asind_q1(-arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* datand - returns the arctan of arg1 (degrees)
*/
Datum
datand(PG_FUNCTION_ARGS)
{
@@ -2049,21 +2049,21 @@ datand(PG_FUNCTION_ARGS)
/*
* The principal branch of the inverse tangent function maps all inputs to
* values in the range [-90, 90], so the result should always be finite,
* even if the input is infinite. Additionally, we take care to ensure
* than when arg1 is 1, the result is exactly 45.
*/
atan_arg1 = atan(arg1);
result = (atan_arg1 / atan_1_0) * 45.0;
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* atan2d - returns the arctan of arg1/arg2 (degrees)
*/
Datum
datan2d(PG_FUNCTION_ARGS)
{
@@ -2083,21 +2083,21 @@ datan2d(PG_FUNCTION_ARGS)
* result should always be finite, even if the inputs are infinite.
*
* Note: this coding assumes that atan(1.0) is a suitable scaling constant
* to get an exact result from atan2(). This might well fail on us at
* some point, requiring us to decide exactly what inputs we think we're
* going to guarantee an exact result for.
*/
atan2_arg1_arg2 = atan2(arg1, arg2);
result = (atan2_arg1_arg2 / atan_1_0) * 45.0;
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* sind_0_to_30 - returns the sine of an angle that lies between 0 and
* 30 degrees. This will return exactly 0 when x is 0,
* and exactly 0.5 when x is 30 degrees.
*/
static double
@@ -2204,21 +2204,21 @@ dcosd(PG_FUNCTION_ARGS)
if (arg1 > 90.0)
{
/* cosd(180-x) = -cosd(x) */
arg1 = 180.0 - arg1;
sign = -sign;
}
result = sign * cosd_q1(arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcotd - returns the cotangent of arg1 (degrees)
*/
Datum
dcotd(PG_FUNCTION_ARGS)
{
@@ -2269,21 +2269,21 @@ dcotd(PG_FUNCTION_ARGS)
result = sign * (cot_arg1 / cot_45);
/*
* On some machines we get cotd(270) = minus zero, but this isn't always
* true. For portability, and because the user constituency for this
* function probably doesn't want minus zero, force it to plain zero.
*/
if (result == 0.0)
result = 0.0;
- check_float8_val(result, true /* cotd(0) == Inf */ , true);
+ CHECKFLOATVAL(result, true /* cotd(0) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/*
* dsind - returns the sine of arg1 (degrees)
*/
Datum
dsind(PG_FUNCTION_ARGS)
{
@@ -2323,21 +2323,21 @@ dsind(PG_FUNCTION_ARGS)
}
if (arg1 > 90.0)
{
/* sind(180-x) = sind(x) */
arg1 = 180.0 - arg1;
}
result = sign * sind_q1(arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dtand - returns the tangent of arg1 (degrees)
*/
Datum
dtand(PG_FUNCTION_ARGS)
{
@@ -2388,21 +2388,21 @@ dtand(PG_FUNCTION_ARGS)
result = sign * (tan_arg1 / tan_45);
/*
* On some machines we get tand(180) = minus zero, but this isn't always
* true. For portability, and because the user constituency for this
* function probably doesn't want minus zero, force it to plain zero.
*/
if (result == 0.0)
result = 0.0;
- check_float8_val(result, true /* tand(90) == Inf */ , true);
+ CHECKFLOATVAL(result, true /* tand(90) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
/*
* degrees - returns degrees converted from radians
*/
Datum
degrees(PG_FUNCTION_ARGS)
{
@@ -2455,21 +2455,21 @@ dsinh(PG_FUNCTION_ARGS)
* sign of arg1.
*/
if (errno == ERANGE)
{
if (arg1 < 0)
result = -get_float8_infinity();
else
result = get_float8_infinity();
}
- check_float8_val(result, true, true);
+ CHECKFLOATVAL(result, true, true);
PG_RETURN_FLOAT8(result);
}
/*
* dcosh - returns the hyperbolic cosine of arg1
*/
Datum
dcosh(PG_FUNCTION_ARGS)
{
@@ -2479,57 +2479,57 @@ dcosh(PG_FUNCTION_ARGS)
errno = 0;
result = cosh(arg1);
/*
* if an ERANGE error occurs, it means there is an overflow. As cosh is
* always positive, it always means the result is positive infinity.
*/
if (errno == ERANGE)
result = get_float8_infinity();
- check_float8_val(result, true, false);
+ CHECKFLOATVAL(result, true, false);
PG_RETURN_FLOAT8(result);
}
/*
* dtanh - returns the hyperbolic tangent of arg1
*/
Datum
dtanh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* For tanh, we don't need an errno check because it never overflows.
*/
result = tanh(arg1);
- check_float8_val(result, false, true);
+ CHECKFLOATVAL(result, false, true);
PG_RETURN_FLOAT8(result);
}
/*
* dasinh - returns the inverse hyperbolic sine of arg1
*/
Datum
dasinh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
/*
* For asinh, we don't need an errno check because it never overflows.
*/
result = asinh(arg1);
- check_float8_val(result, true, true);
+ CHECKFLOATVAL(result, true, true);
PG_RETURN_FLOAT8(result);
}
/*
* dacosh - returns the inverse hyperbolic cosine of arg1
*/
Datum
dacosh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
@@ -2541,21 +2541,21 @@ dacosh(PG_FUNCTION_ARGS)
* thing because some implementations will report that for NaN. Otherwise,
* no error is possible.
*/
if (arg1 < 1.0)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("input is out of range")));
result = acosh(arg1);
- check_float8_val(result, true, true);
+ CHECKFLOATVAL(result, true, true);
PG_RETURN_FLOAT8(result);
}
/*
* datanh - returns the inverse hyperbolic tangent of arg1
*/
Datum
datanh(PG_FUNCTION_ARGS)
{
float8 arg1 = PG_GETARG_FLOAT8(0);
@@ -2576,21 +2576,21 @@ datanh(PG_FUNCTION_ARGS)
* glibc versions may produce the wrong errno for this. All other inputs
* cannot produce an error.
*/
if (arg1 == -1.0)
result = -get_float8_infinity();
else if (arg1 == 1.0)
result = get_float8_infinity();
else
result = atanh(arg1);
- check_float8_val(result, true, true);
+ CHECKFLOATVAL(result, true, true);
PG_RETURN_FLOAT8(result);
}
/*
* drandom - returns a random number
*/
Datum
drandom(PG_FUNCTION_ARGS)
{
@@ -2777,21 +2777,21 @@ float8_combine(PG_FUNCTION_ARGS)
N = N1;
Sx = Sx1;
Sxx = Sxx1;
}
else
{
N = N1 + N2;
Sx = float8_pl(Sx1, Sx2);
tmp = Sx1 / N1 - Sx2 / N2;
Sxx = Sxx1 + Sxx2 + N1 * N2 * tmp * tmp / N;
- check_float8_val(Sxx, isinf(Sxx1) || isinf(Sxx2), true);
+ CHECKFLOATVAL(Sxx, isinf(Sxx1) || isinf(Sxx2), true);
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues1[0] = N;
@@ -3287,27 +3287,27 @@ float8_regr_combine(PG_FUNCTION_ARGS)
Sy = Sy1;
Syy = Syy1;
Sxy = Sxy1;
}
else
{
N = N1 + N2;
Sx = float8_pl(Sx1, Sx2);
tmp1 = Sx1 / N1 - Sx2 / N2;
Sxx = Sxx1 + Sxx2 + N1 * N2 * tmp1 * tmp1 / N;
- check_float8_val(Sxx, isinf(Sxx1) || isinf(Sxx2), true);
+ CHECKFLOATVAL(Sxx, isinf(Sxx1) || isinf(Sxx2), true);
Sy = float8_pl(Sy1, Sy2);
tmp2 = Sy1 / N1 - Sy2 / N2;
Syy = Syy1 + Syy2 + N1 * N2 * tmp2 * tmp2 / N;
- check_float8_val(Syy, isinf(Syy1) || isinf(Syy2), true);
+ CHECKFLOATVAL(Syy, isinf(Syy1) || isinf(Syy2), true);
Sxy = Sxy1 + Sxy2 + N1 * N2 * tmp1 * tmp2 / N;
- check_float8_val(Sxy, isinf(Sxy1) || isinf(Sxy2), true);
+ CHECKFLOATVAL(Sxy, isinf(Sxy1) || isinf(Sxy2), true);
}
/*
* If we're invoked as an aggregate, we can cheat and modify our first
* parameter in-place to reduce palloc overhead. Otherwise we construct a
* new array with the updated transition data and return it.
*/
if (AggCheckCallContext(fcinfo, NULL))
{
transvalues1[0] = N;
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index d5ded471c4..a0d02c6a90 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -5538,14 +5538,14 @@ pg_hypot(float8 x, float8 y)
* such cases, but more importantly it also protects against
* divide-by-zero errors, since now x >= y.
*/
if (y == 0.0)
return x;
/* Determine the hypotenuse */
yx = y / x;
result = x * sqrt(1.0 + (yx * yx));
- check_float8_val(result, false, false);
+ CHECKFLOATVAL(result, false, false);
return result;
}
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index e2c5dc0f57..5c6ce5bdc6 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -25,20 +25,43 @@
/* Radians per degree, a.k.a. PI / 180 */
#define RADIANS_PER_DEGREE 0.0174532925199432957692
/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. */
#if defined(WIN32) && !defined(NAN)
static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
#define NAN (*(const float8 *) nan)
#endif
+/*
+ * Checks to see if a float4/8 val has underflowed or overflowed
+ *
+ * The rest of the API uses inline functions, but this has to stay as macro
+ * to prevent the inf_is_valid and zero_is_valid arguments to be evaluated
+ * when the val is not inf or zero. Evaluating the arguments is more
+ * expensive than checking the value itself.
+ *
+ * Note that this macro double evaluates the val.
+ */
+#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \
+do { \
+ if (unlikely(isinf(val) && !(inf_is_valid))) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
+ errmsg("value out of range: overflow"))); \
+ \
+ if (unlikely((val) == 0.0 && !(zero_is_valid))) \
+ ereport(ERROR, \
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
+ errmsg("value out of range: underflow"))); \
+} while(0)
+
extern PGDLLIMPORT int extra_float_digits;
/*
* Utility functions in float.c
*/
extern int is_infinite(float8 val);
extern float8 float8in_internal(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
extern float8 float8in_internal_opt_error(char *num, char **endptr_p,
const char *type_name, const char *orig_string,
@@ -122,159 +145,125 @@ get_float8_nan(void)
#if defined(NAN) && !(defined(__NetBSD__) && defined(__mips__))
/* C99 standard way */
return (float8) NAN;
#else
/* Assume we can get a NaN via zero divide */
return (float8) (0.0 / 0.0);
#endif
}
/*
- * Checks to see if a float4/8 val has underflowed or overflowed
- */
-
-static inline void
-check_float4_val(const float4 val, const bool inf_is_valid,
- const bool zero_is_valid)
-{
- if (!inf_is_valid && unlikely(isinf(val)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("value out of range: overflow")));
-
- if (!zero_is_valid && unlikely(val == 0.0))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("value out of range: underflow")));
-}
-
-static inline void
-check_float8_val(const float8 val, const bool inf_is_valid,
- const bool zero_is_valid)
-{
- if (!inf_is_valid && unlikely(isinf(val)))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("value out of range: overflow")));
-
- if (!zero_is_valid && unlikely(val == 0.0))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("value out of range: underflow")));
-}
-
-/*
- * Routines for operations with the checks above
+ * Routines for operations with overflow/underflow checks
*
* There isn't any way to check for underflow of addition/subtraction
* because numbers near the underflow value have already been rounded to
* the point where we can't detect that the two values were originally
* different, e.g. on x86, '1e-45'::float4 == '2e-45'::float4 ==
* 1.4013e-45.
*/
static inline float4
float4_pl(const float4 val1, const float4 val2)
{
float4 result;
result = val1 + val2;
- check_float4_val(result, isinf(val1) || isinf(val2), true);
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true);
return result;
}
static inline float8
float8_pl(const float8 val1, const float8 val2)
{
float8 result;
result = val1 + val2;
- check_float8_val(result, isinf(val1) || isinf(val2), true);
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true);
return result;
}
static inline float4
float4_mi(const float4 val1, const float4 val2)
{
float4 result;
result = val1 - val2;
- check_float4_val(result, isinf(val1) || isinf(val2), true);
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true);
return result;
}
static inline float8
float8_mi(const float8 val1, const float8 val2)
{
float8 result;
result = val1 - val2;
- check_float8_val(result, isinf(val1) || isinf(val2), true);
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), true);
return result;
}
static inline float4
float4_mul(const float4 val1, const float4 val2)
{
float4 result;
result = val1 * val2;
- check_float4_val(result, isinf(val1) || isinf(val2),
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2),
val1 == 0.0f || val2 == 0.0f);
return result;
}
static inline float8
float8_mul(const float8 val1, const float8 val2)
{
float8 result;
result = val1 * val2;
- check_float8_val(result, isinf(val1) || isinf(val2),
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2),
val1 == 0.0 || val2 == 0.0);
return result;
}
static inline float4
float4_div(const float4 val1, const float4 val2)
{
float4 result;
if (val2 == 0.0f)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = val1 / val2;
- check_float4_val(result, isinf(val1) || isinf(val2), val1 == 0.0f);
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), val1 == 0.0f);
return result;
}
static inline float8
float8_div(const float8 val1, const float8 val2)
{
float8 result;
if (val2 == 0.0)
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));
result = val1 / val2;
- check_float8_val(result, isinf(val1) || isinf(val2), val1 == 0.0);
+ CHECKFLOATVAL(result, isinf(val1) || isinf(val2), val1 == 0.0);
return result;
}
/*
* Routines for NaN-aware comparisons
*
* We consider all NaNs to be equal and larger than any non-NaN. This is
* somewhat arbitrary; the important thing is to have a consistent sort
* order.
--
2.17.1