diff options
-rw-r--r-- | src/backend/utils/adt/float.c | 163 | ||||
-rw-r--r-- | src/backend/utils/adt/geo_ops.c | 5 | ||||
-rw-r--r-- | src/include/utils/float.h | 66 |
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; } |