> > > > For most places it'd probably end up being easier to read and to
> > > > optimize if we just wrote them as
> > > > if (unlikely(isinf(result)) && !isinf(arg))
> > > >     float_overflow_error();
> > > > and when needed added a
> > > > else if (unlikely(result == 0) && arg1 != 0.0)
> > > >     float_underflow_error();
> > >
> > > +1
> >
> > Cool. Emre, any chance you could write a patch along those lines?
>
> Yes, I am happy to do.  It makes more sense to me too.

How about the one attached?
From 161384d51f517f1f4d9f403b46e90fbe1c869cbe Mon Sep 17 00:00:00 2001
From: Emre Hasegeli <e...@hasegeli.com>
Date: Fri, 7 Feb 2020 10:27:25 +0000
Subject: [PATCH] Optimize float overflow/underflow checks

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
inlines the checks to prevent the performance regression.

This also moves the error messages further out of line.  All these
otherwise very small functions having their own ereports() was making
them much bigger.  Our low code density, and the resulting rate of ITLB
misses, is pretty significant cost.

And also this commit is changing the usage of unlikely() to cover
the whole condition.  Using it only for the result is not semantically
correct.  It is more than likely for the result to be infinite when
the input is, or it to be 0 when the input is.

Reported-by: Keisuke Kuroda <keisuke.kuroda.3...@gmail.com>
Discussion: https://postgr.es/m/CANDwggLe1Gc1OrRqvPfGE%3DkM9K0FSfia0hbeFCEmwabhLz95AA%40mail.gmail.com
---
 src/backend/utils/adt/float.c   | 146 ++++++++++++++++++++++----------
 src/backend/utils/adt/geo_ops.c |   5 +-
 src/include/utils/float.h       |  67 ++++++++-------
 3 files changed, 145 insertions(+), 73 deletions(-)

diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index a90d4db215..30abe06b87 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -79,20 +79,35 @@ static void init_degree_constants(void);
  * function, which causes configure to not set HAVE_CBRT.  Furthermore,
  * their compilers spit up at the mismatch between extern declaration
  * and static definition.  We work around that here by the expedient
  * of a #define to make the actual name of the static function different.
  */
 #define cbrt my_cbrt
 static double cbrt(double x);
 #endif							/* HAVE_CBRT */
 
 
+void float_overflow_error()
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+			 errmsg("value out of range: overflow")));
+}
+
+void float_underflow_error()
+{
+	ereport(ERROR,
+			(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+			 errmsg("value out of range: underflow")));
+}
+
+
 /*
  * Returns -1 if 'val' represents negative infinity, 1 if 'val'
  * represents (positive) infinity, and 0 otherwise. On some platforms,
  * this is equivalent to the isinf() macro, but not everywhere: C99
  * does not specify that isinf() needs to distinguish between positive
  * and negative infinity.
  */
 int
 is_infinite(double val)
 {
@@ -1183,24 +1198,29 @@ ftod(PG_FUNCTION_ARGS)
 }
 
 
 /*
  *		dtof			- converts a float8 number to a float4 number
  */
 Datum
 dtof(PG_FUNCTION_ARGS)
 {
 	float8		num = PG_GETARG_FLOAT8(0);
+	float4		result;
 
-	check_float4_val((float4) num, isinf(num), num == 0);
+	result = (float4) num;
+	if (unlikely(isinf(result) && !isinf(num)))
+		float_overflow_error();
+	if (unlikely(result == 0.0f && num != 0.0f))
+		float_underflow_error();
 
-	PG_RETURN_FLOAT4((float4) num);
+	PG_RETURN_FLOAT4(result);
 }
 
 
 /*
  *		dtoi4			- converts a float8 number to an int4 number
  */
 Datum
 dtoi4(PG_FUNCTION_ARGS)
 {
 	float8		num = PG_GETARG_FLOAT8(0);
@@ -1437,37 +1457,44 @@ 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);
+	if (unlikely(isinf(result) && !isinf(arg1)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && arg1 != 0.0))
+		float_underflow_error();
 
-	check_float8_val(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);
+	if (unlikely(isinf(result) && !isinf(arg1)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && arg1 != 0.0))
+		float_underflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dpow			- returns pow(arg1,arg2)
  */
 Datum
 dpow(PG_FUNCTION_ARGS)
 {
@@ -1525,40 +1552,48 @@ 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);
+	if (unlikely(isinf(result) && !isinf(arg1) && !isinf(arg2)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && arg1 != 0.0))
+		float_underflow_error();
+
 	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);
+	if (unlikely(isinf(result) && !isinf(arg1)))
+		float_overflow_error();
+	if (unlikely(result == 0.0))
+		float_underflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dlog1			- returns the natural logarithm of arg1
  */
 Datum
 dlog1(PG_FUNCTION_ARGS)
 {
@@ -1572,22 +1607,25 @@ dlog1(PG_FUNCTION_ARGS)
 	if (arg1 == 0.0)
 		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);
+	if (unlikely(isinf(result) && !isinf(arg1)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && arg1 != 1.0))
+		float_underflow_error();
 
-	check_float8_val(result, isinf(arg1), arg1 == 1);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dlog10			- returns the base 10 logarithm of arg1
  */
 Datum
 dlog10(PG_FUNCTION_ARGS)
 {
@@ -1602,22 +1640,25 @@ dlog10(PG_FUNCTION_ARGS)
 	if (arg1 == 0.0)
 		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);
+	if (unlikely(isinf(result) && !isinf(arg1)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && arg1 != 1.0))
+		float_underflow_error();
 
-	check_float8_val(result, isinf(arg1), arg1 == 1);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dacos			- returns the arccos of arg1 (radians)
  */
 Datum
 dacos(PG_FUNCTION_ARGS)
 {
@@ -1632,22 +1673,23 @@ dacos(PG_FUNCTION_ARGS)
 	 * The principal branch of the inverse cosine function maps values in the
 	 * 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
 
-	check_float8_val(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dasin			- returns the arcsin of arg1 (radians)
  */
 Datum
 dasin(PG_FUNCTION_ARGS)
 {
@@ -1662,22 +1704,23 @@ dasin(PG_FUNCTION_ARGS)
 	 * The principal branch of the inverse sine function maps values in the
 	 * 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
 
-	check_float8_val(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		datan			- returns the arctan of arg1 (radians)
  */
 Datum
 datan(PG_FUNCTION_ARGS)
 {
@@ -1687,22 +1730,23 @@ datan(PG_FUNCTION_ARGS)
 	/* Per the POSIX spec, return NaN if the input is NaN */
 	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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
 
-	check_float8_val(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		atan2			- returns the arctan of arg1/arg2 (radians)
  */
 Datum
 datan2(PG_FUNCTION_ARGS)
 {
@@ -1712,22 +1756,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
 
-	check_float8_val(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dcos			- returns the cosine of arg1 (radians)
  */
 Datum
 dcos(PG_FUNCTION_ARGS)
 {
@@ -1752,22 +1797,23 @@ dcos(PG_FUNCTION_ARGS)
 	 * should return a domain error; but we won't notice that unless the
 	 * 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")));
+	if (unlikely(isinf(result)))
+		float_overflow_error();
 
-	check_float8_val(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dcot			- returns the cotangent of arg1 (radians)
  */
 Datum
 dcot(PG_FUNCTION_ARGS)
 {
@@ -1780,21 +1826,22 @@ 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);
+	/* Not checking for overflow because cot(0) == Inf */
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dsin			- returns the sine of arg1 (radians)
  */
 Datum
 dsin(PG_FUNCTION_ARGS)
 {
@@ -1805,22 +1852,23 @@ dsin(PG_FUNCTION_ARGS)
 	if (isnan(arg1))
 		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")));
+	if (unlikely(isinf(result)))
+		float_overflow_error();
 
-	check_float8_val(result, false, true);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dtan			- returns the tangent of arg1 (radians)
  */
 Datum
 dtan(PG_FUNCTION_ARGS)
 {
@@ -1831,22 +1879,22 @@ dtan(PG_FUNCTION_ARGS)
 	if (isnan(arg1))
 		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")));
+	/* Not checking for overflow because tan(pi/2) == Inf */
 
-	check_float8_val(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 +2032,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dasind			- returns the arcsin of arg1 (degrees)
  */
 Datum
 dasind(PG_FUNCTION_ARGS)
 {
@@ -2019,21 +2069,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		datand			- returns the arctan of arg1 (degrees)
  */
 Datum
 datand(PG_FUNCTION_ARGS)
 {
@@ -2049,21 +2101,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		atan2d			- returns the arctan of arg1/arg2 (degrees)
  */
 Datum
 datan2d(PG_FUNCTION_ARGS)
 {
@@ -2083,21 +2137,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	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 +2260,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dcotd			- returns the cotangent of arg1 (degrees)
  */
 Datum
 dcotd(PG_FUNCTION_ARGS)
 {
@@ -2269,21 +2327,22 @@ 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);
+	/* Not checking for overflow because cotd(0) == Inf */
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dsind			- returns the sine of arg1 (degrees)
  */
 Datum
 dsind(PG_FUNCTION_ARGS)
 {
@@ -2323,21 +2382,23 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dtand			- returns the tangent of arg1 (degrees)
  */
 Datum
 dtand(PG_FUNCTION_ARGS)
 {
@@ -2388,21 +2449,22 @@ 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);
+	/* Not checking for overflow because tand(90) == Inf */
+
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		degrees		- returns degrees converted from radians
  */
 Datum
 degrees(PG_FUNCTION_ARGS)
 {
@@ -2455,21 +2517,20 @@ 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);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		dcosh			- returns the hyperbolic cosine of arg1
  */
 Datum
 dcosh(PG_FUNCTION_ARGS)
 {
@@ -2479,57 +2540,60 @@ 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);
+	if (unlikely(result == 0.0))
+		float_underflow_error();
+
 	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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+
 	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);
 	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 +2605,20 @@ 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);
 	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 +2639,20 @@ 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);
 	PG_RETURN_FLOAT8(result);
 }
 
 
 /*
  *		drandom		- returns a random number
  */
 Datum
 drandom(PG_FUNCTION_ARGS)
 {
@@ -2777,21 +2839,22 @@ 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);
+		if (unlikely(isinf(Sxx) && !isinf(Sxx1) && !isinf(Sxx2)))
+			float_overflow_error();
 	}
 
 	/*
 	 * 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;
@@ -2846,23 +2909,21 @@ float8_accum(PG_FUNCTION_ARGS)
 
 		/*
 		 * Overflow check.  We only report an overflow error when finite
 		 * inputs lead to infinite results.  Note also that Sxx should be NaN
 		 * if any of the inputs are infinite, so we intentionally prevent Sxx
 		 * from becoming infinite.
 		 */
 		if (isinf(Sx) || isinf(Sxx))
 		{
 			if (!isinf(transvalues[1]) && !isinf(newval))
-				ereport(ERROR,
-						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-						 errmsg("value out of range: overflow")));
+				float_overflow_error();
 
 			Sxx = get_float8_nan();
 		}
 	}
 
 	/*
 	 * 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.
 	 */
@@ -2922,23 +2983,21 @@ float4_accum(PG_FUNCTION_ARGS)
 
 		/*
 		 * Overflow check.  We only report an overflow error when finite
 		 * inputs lead to infinite results.  Note also that Sxx should be NaN
 		 * if any of the inputs are infinite, so we intentionally prevent Sxx
 		 * from becoming infinite.
 		 */
 		if (isinf(Sx) || isinf(Sxx))
 		{
 			if (!isinf(transvalues[1]) && !isinf(newval))
-				ereport(ERROR,
-						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-						 errmsg("value out of range: overflow")));
+				float_overflow_error();
 
 			Sxx = get_float8_nan();
 		}
 	}
 
 	/*
 	 * 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.
 	 */
@@ -3145,23 +3204,21 @@ float8_regr_accum(PG_FUNCTION_ARGS)
 		 */
 		if (isinf(Sx) || isinf(Sxx) || isinf(Sy) || isinf(Syy) || isinf(Sxy))
 		{
 			if (((isinf(Sx) || isinf(Sxx)) &&
 				 !isinf(transvalues[1]) && !isinf(newvalX)) ||
 				((isinf(Sy) || isinf(Syy)) &&
 				 !isinf(transvalues[3]) && !isinf(newvalY)) ||
 				(isinf(Sxy) &&
 				 !isinf(transvalues[1]) && !isinf(newvalX) &&
 				 !isinf(transvalues[3]) && !isinf(newvalY)))
-				ereport(ERROR,
-						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
-						 errmsg("value out of range: overflow")));
+				float_overflow_error();
 
 			if (isinf(Sxx))
 				Sxx = get_float8_nan();
 			if (isinf(Syy))
 				Syy = get_float8_nan();
 			if (isinf(Sxy))
 				Sxy = get_float8_nan();
 		}
 	}
 
@@ -3287,27 +3344,30 @@ 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);
+		if (unlikely(isinf(Sxx) && !isinf(Sxx1) && !isinf(Sxx2)))
+			float_overflow_error();
 		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);
+		if (unlikely(isinf(Syy) && !isinf(Syy1) && !isinf(Syy2)))
+			float_overflow_error();
 		Sxy = Sxy1 + Sxy2 + N1 * N2 * tmp1 * tmp2 / N;
-		check_float8_val(Sxy, isinf(Sxy1) || isinf(Sxy2), true);
+		if (unlikely(isinf(Sxy) && !isinf(Sxy1) && !isinf(Sxy2)))
+			float_overflow_error();
 	}
 
 	/*
 	 * 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..71ee7f8f08 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -5538,14 +5538,17 @@ 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);
+	if (unlikely(isinf(result)))
+		float_overflow_error();
+	if (unlikely(result == 0.0))
+		float_underflow_error();
 
 	return result;
 }
diff --git a/src/include/utils/float.h b/src/include/utils/float.h
index e2c5dc0f57..0018b3b5af 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -30,20 +30,22 @@
 static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
 
 #define NAN (*(const float8 *) nan)
 #endif
 
 extern PGDLLIMPORT int extra_float_digits;
 
 /*
  * Utility functions in float.c
  */
+extern void	float_overflow_error(void);
+extern void	float_underflow_error(void);
 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,
 										  bool *have_error);
 extern char *float8out_internal(float8 num);
 extern int	float4_cmp_internal(float4 a, float4 b);
 extern int	float8_cmp_internal(float8 a, float8 b);
 
@@ -123,158 +125,165 @@ get_float8_nan(void)
 	/* 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
+ *
+ * They are no longer used because of the performance regression they cause,
+ * but left for backwards compatibility.
  */
 
 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")));
+	if (unlikely(isinf(val) && !inf_is_valid))
+		float_overflow_error();
+	if (unlikely(val == 0.0f && !zero_is_valid))
+		float_underflow_error();
 }
 
 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")));
+	if (unlikely(isinf(val) && !inf_is_valid))
+		float_overflow_error();
+	if (unlikely(val == 0.0 && !zero_is_valid))
+		float_underflow_error();
 }
 
 /*
- * 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);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
 
 	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);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
 
 	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);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
 
 	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);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
 
 	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),
-					 val1 == 0.0f || val2 == 0.0f);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
+	if (unlikely(result == 0.0f && val1 != 0.0f && val2 != 0.0f))
+		float_underflow_error();
 
 	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),
-					 val1 == 0.0 || val2 == 0.0);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && val1 != 0.0 && val2 != 0.0))
+		float_underflow_error();
 
 	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);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
+	if (unlikely(result == 0.0f && val1 != 0.0f))
+		float_underflow_error();
 
 	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);
+	if (unlikely(isinf(result) && !isinf(val1) && !isinf(val2)))
+		float_overflow_error();
+	if (unlikely(result == 0.0 && val1 != 0.0))
+		float_underflow_error();
 
 	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