> 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?

Added.

> 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).

I don't know why those trigonometric functions don't check for
overflow/underflow like all the rest of float.c.  I'll submit another
patch to make them error when overflow/underflow.

The new version is attached.
From fb5052b869255ef9465b1de92e84b2fb66dd6eb3 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
---
 contrib/btree_gist/btree_utils_num.h |  6 +--
 src/backend/utils/adt/float.c        | 66 ++++++++++++------------
 src/backend/utils/adt/geo_ops.c      |  2 +-
 src/include/utils/float.h            | 76 ++++++++++++----------------
 4 files changed, 70 insertions(+), 80 deletions(-)

diff --git a/contrib/btree_gist/btree_utils_num.h b/contrib/btree_gist/btree_utils_num.h
index 1fedfbe82d..a3227fd758 100644
--- a/contrib/btree_gist/btree_utils_num.h
+++ b/contrib/btree_gist/btree_utils_num.h
@@ -84,30 +84,30 @@ typedef struct
  */
 #define INTERVAL_TO_SEC(ivp) \
 	(((double) (ivp)->time) / ((double) USECS_PER_SEC) + \
 	 (ivp)->day * (24.0 * SECS_PER_HOUR) + \
 	 (ivp)->month * (30.0 * SECS_PER_DAY))
 
 #define GET_FLOAT_DISTANCE(t, arg1, arg2)	Abs( ((float8) *((const t *) (arg1))) - ((float8) *((const t *) (arg2))) )
 
 /*
  * check to see if a float4/8 val has underflowed or overflowed
- * borrowed from src/backend/utils/adt/float.c
+ * borrowed from src/include/utils/float.c
  */
 #define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid)			\
 do {															\
-	if (isinf(val) && !(inf_is_valid))							\
+	if (unlikely(isinf(val) && !(inf_is_valid)))				\
 		ereport(ERROR,											\
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),	\
 		  errmsg("value out of range: overflow")));				\
 																\
-	if ((val) == 0.0 && !(zero_is_valid))						\
+	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 Interval *abs_interval(Interval *a);
 
 extern bool gbt_num_consistent(const GBT_NUMKEY_R *key, const void *query,
 							   const StrategyNumber *strategy, bool is_leaf,
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..f9c0222c56 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -25,20 +25,44 @@
 /* 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.  We first check the val, and then
+ * the other arguments.  Evaluation the other arguments is twice as expensive
+ * on the common code paths.
+ *
+ * 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 +146,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

Reply via email to