aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/utils/adt/float.c163
-rw-r--r--src/backend/utils/adt/geo_ops.c5
-rw-r--r--src/include/utils/float.h66
3 files changed, 165 insertions, 69 deletions
diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c
index f2521999ac0..58194ccbdac 100644
--- a/src/backend/utils/adt/float.c
+++ b/src/backend/utils/adt/float.c
@@ -87,6 +87,38 @@ static double cbrt(double x);
/*
+ * We use these out-of-line ereport() calls to report float overflow,
+ * underflow, and zero-divide, because following our usual practice of
+ * repeating them at each call site would lead to a lot of code bloat.
+ *
+ * This does mean that you don't get a useful error location indicator.
+ */
+pg_noinline void
+float_overflow_error(void)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: overflow")));
+}
+
+pg_noinline void
+float_underflow_error(void)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("value out of range: underflow")));
+}
+
+pg_noinline void
+float_zero_divide_error(void)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_DIVISION_BY_ZERO),
+ errmsg("division by zero")));
+}
+
+
+/*
* 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
@@ -1184,10 +1216,15 @@ 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.0)
+ float_underflow_error();
- PG_RETURN_FLOAT4((float4) num);
+ PG_RETURN_FLOAT4(result);
}
@@ -1438,8 +1475,11 @@ dsqrt(PG_FUNCTION_ARGS)
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);
}
@@ -1454,7 +1494,11 @@ dcbrt(PG_FUNCTION_ARGS)
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);
}
@@ -1526,7 +1570,11 @@ dpow(PG_FUNCTION_ARGS)
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);
}
@@ -1545,7 +1593,11 @@ dexp(PG_FUNCTION_ARGS)
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);
}
@@ -1573,8 +1625,11 @@ dlog1(PG_FUNCTION_ARGS)
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);
}
@@ -1603,8 +1658,11 @@ dlog10(PG_FUNCTION_ARGS)
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);
}
@@ -1633,8 +1691,9 @@ dacos(PG_FUNCTION_ARGS)
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);
}
@@ -1663,8 +1722,9 @@ dasin(PG_FUNCTION_ARGS)
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);
}
@@ -1688,8 +1748,9 @@ datan(PG_FUNCTION_ARGS)
* 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);
}
@@ -1713,8 +1774,9 @@ datan2(PG_FUNCTION_ARGS)
* 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);
}
@@ -1753,8 +1815,9 @@ dcos(PG_FUNCTION_ARGS)
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);
}
@@ -1781,7 +1844,8 @@ dcot(PG_FUNCTION_ARGS)
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);
}
@@ -1806,8 +1870,9 @@ dsin(PG_FUNCTION_ARGS)
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);
}
@@ -1832,8 +1897,8 @@ dtan(PG_FUNCTION_ARGS)
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);
}
@@ -1985,7 +2050,9 @@ dacosd(PG_FUNCTION_ARGS)
else
result = 90.0 + asind_q1(-arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
@@ -2020,7 +2087,9 @@ dasind(PG_FUNCTION_ARGS)
else
result = -asind_q1(-arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
@@ -2050,7 +2119,9 @@ datand(PG_FUNCTION_ARGS)
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);
}
@@ -2084,7 +2155,9 @@ datan2d(PG_FUNCTION_ARGS)
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);
}
@@ -2205,7 +2278,9 @@ dcosd(PG_FUNCTION_ARGS)
result = sign * cosd_q1(arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
@@ -2270,7 +2345,8 @@ dcotd(PG_FUNCTION_ARGS)
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);
}
@@ -2324,7 +2400,9 @@ dsind(PG_FUNCTION_ARGS)
result = sign * sind_q1(arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
@@ -2389,7 +2467,8 @@ dtand(PG_FUNCTION_ARGS)
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);
}
@@ -2456,7 +2535,6 @@ dsinh(PG_FUNCTION_ARGS)
result = get_float8_infinity();
}
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
@@ -2480,7 +2558,9 @@ dcosh(PG_FUNCTION_ARGS)
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);
}
@@ -2498,7 +2578,9 @@ dtanh(PG_FUNCTION_ARGS)
*/
result = tanh(arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
@@ -2516,7 +2598,6 @@ dasinh(PG_FUNCTION_ARGS)
*/
result = asinh(arg1);
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
@@ -2542,7 +2623,6 @@ dacosh(PG_FUNCTION_ARGS)
result = acosh(arg1);
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
@@ -2577,7 +2657,6 @@ datanh(PG_FUNCTION_ARGS)
else
result = atanh(arg1);
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
@@ -2778,7 +2857,8 @@ float8_combine(PG_FUNCTION_ARGS)
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();
}
/*
@@ -2847,9 +2927,7 @@ float8_accum(PG_FUNCTION_ARGS)
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();
}
@@ -2923,9 +3001,7 @@ float4_accum(PG_FUNCTION_ARGS)
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();
}
@@ -3146,9 +3222,7 @@ float8_regr_accum(PG_FUNCTION_ARGS)
(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();
@@ -3288,13 +3362,16 @@ float8_regr_combine(PG_FUNCTION_ARGS)
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();
}
/*
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index 373784fcc1e..f8e74a00762 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -5429,7 +5429,10 @@ pg_hypot(float8 x, float8 y)
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 543d00e5910..e5acc0f0aa4 100644
--- a/src/include/utils/float.h
+++ b/src/include/utils/float.h
@@ -37,6 +37,9 @@ extern PGDLLIMPORT int extra_float_digits;
/*
* Utility functions in float.c
*/
+extern void float_overflow_error(void) pg_attribute_noreturn();
+extern void float_underflow_error(void) pg_attribute_noreturn();
+extern void float_zero_divide_error(void) pg_attribute_noreturn();
extern int is_infinite(float8 val);
extern float8 float8in_internal(char *num, char **endptr_p,
const char *type_name, const char *orig_string);
@@ -130,18 +133,23 @@ get_float8_nan(void)
/*
* Checks to see if a float4/8 val has underflowed or overflowed
+ *
+ * These subroutines are no longer used in the core code because of the
+ * performance issues they cause, but they remain for now for backwards
+ * compatibility for extensions. Note that we can't rely on
+ * float_over/underflow_error() to exist in v12, so don't call those here.
*/
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)))
+ if (unlikely(isinf(val)) && !inf_is_valid)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value out of range: overflow")));
- if (!zero_is_valid && unlikely(val == 0.0))
+ if (unlikely(val == 0.0f) && !zero_is_valid)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value out of range: underflow")));
@@ -151,19 +159,19 @@ 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)))
+ if (unlikely(isinf(val)) && !inf_is_valid)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value out of range: overflow")));
- if (!zero_is_valid && unlikely(val == 0.0))
+ if (unlikely(val == 0.0) && !zero_is_valid)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value out of range: underflow")));
}
/*
- * Routines for operations with the checks above
+ * Floating-point arithmetic with overflow/underflow reported as errors
*
* There isn't any way to check for underflow of addition/subtraction
* because numbers near the underflow value have already been rounded to
@@ -178,7 +186,8 @@ 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;
}
@@ -189,7 +198,8 @@ 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;
}
@@ -200,7 +210,8 @@ 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;
}
@@ -211,7 +222,8 @@ 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;
}
@@ -222,8 +234,10 @@ 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;
}
@@ -234,8 +248,10 @@ 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;
}
@@ -245,13 +261,13 @@ 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")));
-
+ if (unlikely(val2 == 0.0f))
+ float_zero_divide_error();
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;
}
@@ -261,13 +277,13 @@ 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")));
-
+ if (unlikely(val2 == 0.0))
+ float_zero_divide_error();
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;
}