#endif /* HAVE_CBRT */
+/*
+ * 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,
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
* 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);
}
* 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);
}
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);
}
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);
}
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);
}
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);
}
else
result = 90.0 + asind_q1(-arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
else
result = -asind_q1(-arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
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);
}
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);
}
result = sign * cosd_q1(arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
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);
}
result = sign * sind_q1(arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
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);
}
result = get_float8_infinity();
}
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
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);
}
*/
result = tanh(arg1);
- check_float8_val(result, false, true);
+ if (unlikely(isinf(result)))
+ float_overflow_error();
+
PG_RETURN_FLOAT8(result);
}
*/
result = asinh(arg1);
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
result = acosh(arg1);
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
else
result = atanh(arg1);
- check_float8_val(result, true, true);
PG_RETURN_FLOAT8(result);
}
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 (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 (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();
}
(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();
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();
}
/*
/*
* 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);
}
/*
- * 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
+ * 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
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
{
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;
}
{
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;
}