diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 2b578ddf4e6ab38b12a2b1442aefdfdff285a4a4..4c081540a6f9516c3f05de4482761e405d6e5d33 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -8,52 +8,13 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.131 2006/12/23 02:13:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.132 2007/01/02 20:00:49 momjian Exp $ * *------------------------------------------------------------------------- */ -/*---------- - * OLD COMMENTS - * Basic float4 ops: - * float4in, float4out, float4recv, float4send - * float4abs, float4um, float4up - * Basic float8 ops: - * float8in, float8out, float8recv, float8send - * float8abs, float8um, float8up - * Arithmetic operators: - * float4pl, float4mi, float4mul, float4div - * float8pl, float8mi, float8mul, float8div - * Comparison operators: - * float4eq, float4ne, float4lt, float4le, float4gt, float4ge, float4cmp - * float8eq, float8ne, float8lt, float8le, float8gt, float8ge, float8cmp - * Conversion routines: - * ftod, dtof, i4tod, dtoi4, i2tod, dtoi2, itof, ftoi, i2tof, ftoi2 - * - * Random float8 ops: - * dround, dtrunc, dsqrt, dcbrt, dpow, dexp, dlog1 - * Arithmetic operators: - * float48pl, float48mi, float48mul, float48div - * float84pl, float84mi, float84mul, float84div - * Comparison operators: - * float48eq, float48ne, float48lt, float48le, float48gt, float48ge - * float84eq, float84ne, float84lt, float84le, float84gt, float84ge - * - * (You can do the arithmetic and comparison stuff using conversion - * routines, but then you pay the overhead of invoking a separate - * conversion function...) - * - * XXX GLUESOME STUFF. FIX IT! -AY '94 - * - * Added some additional conversion routines and cleaned up - * a bit of the existing code. Need to change the error checking - * for calls to pow(), exp() since on some machines (my Linux box - * included) these routines do not set errno. - tgl 97/05/10 - *---------- - */ #include "postgres.h" #include -#include #include #include /* for finite() on Solaris */ @@ -91,21 +52,30 @@ static const uint32 nan[2] = {0xffffffff, 0x7fffffff}; #define MAXFLOATWIDTH 64 #define MAXDOUBLEWIDTH 128 -/* ========== USER I/O ROUTINES ========== */ +/* + * check to see if a float4/8 val has underflowed or overflowed + */ +#define CHECKFLOATVAL(val, inf_is_valid, zero_is_valid) \ +do { \ + if (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)) \ + ereport(ERROR, \ + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \ + errmsg("value out of range: underflow"))); \ +} while(0) -#define FLOAT4_MAX FLT_MAX -#define FLOAT4_MIN FLT_MIN -#define FLOAT8_MAX DBL_MAX -#define FLOAT8_MIN DBL_MIN +/* ========== USER I/O ROUTINES ========== */ /* Configurable GUC parameter */ int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */ -static void CheckFloat4Val(double val); -static void CheckFloat8Val(double val); static int float4_cmp_internal(float4 a, float4 b); static int float8_cmp_internal(float8 a, float8 b); @@ -204,44 +174,6 @@ is_infinite(double val) } -/* - * check to see if a float4 val is outside of the FLOAT4_MIN, - * FLOAT4_MAX bounds. - * - * raise an ereport() error if it is - */ -static void -CheckFloat4Val(double val) -{ - if (fabs(val) > FLOAT4_MAX) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("type \"real\" value out of range: overflow"))); - if (val != 0.0 && fabs(val) < FLOAT4_MIN) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("type \"real\" value out of range: underflow"))); -} - -/* - * check to see if a float8 val is outside of the FLOAT8_MIN, - * FLOAT8_MAX bounds. - * - * raise an ereport() error if it is - */ -static void -CheckFloat8Val(double val) -{ - if (fabs(val) > FLOAT8_MAX) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("type \"double precision\" value out of range: overflow"))); - if (val != 0.0 && fabs(val) < FLOAT8_MIN) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("type \"double precision\" value out of range: underflow"))); -} - /* * float4in - converts "num" to float * restricted syntax: @@ -369,8 +301,7 @@ float4in(PG_FUNCTION_ARGS) * if we get here, we have a legal double, still need to check to see if * it's a legal float4 */ - if (!isinf(val)) - CheckFloat4Val(val); + CHECKFLOATVAL((float4) val, isinf(val), val == 0); PG_RETURN_FLOAT4((float4) val); } @@ -558,8 +489,7 @@ float8in(PG_FUNCTION_ARGS) errmsg("invalid input syntax for type double precision: \"%s\"", orig_num))); - if (!isinf(val)) - CheckFloat8Val(val); + CHECKFLOATVAL(val, true, true); PG_RETURN_FLOAT8(val); } @@ -652,8 +582,12 @@ Datum float4um(PG_FUNCTION_ARGS) { float4 arg1 = PG_GETARG_FLOAT4(0); + float4 result; + + result = ((arg1 != 0) ? -(arg1) : arg1); - PG_RETURN_FLOAT4((float4) -arg1); + CHECKFLOATVAL(result, isinf(arg1), true); + PG_RETURN_FLOAT4(result); } Datum @@ -705,12 +639,8 @@ Datum float8abs(PG_FUNCTION_ARGS) { float8 arg1 = PG_GETARG_FLOAT8(0); - float8 result; - - result = fabs(arg1); - CheckFloat8Val(result); - PG_RETURN_FLOAT8(result); + PG_RETURN_FLOAT8(fabs(arg1)); } @@ -725,7 +655,7 @@ float8um(PG_FUNCTION_ARGS) result = ((arg1 != 0) ? -(arg1) : arg1); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result); } @@ -781,13 +711,20 @@ float8smaller(PG_FUNCTION_ARGS) Datum float4pl(PG_FUNCTION_ARGS) { - float4 arg1 = PG_GETARG_FLOAT4(0); - float4 arg2 = PG_GETARG_FLOAT4(1); - double result; + float8 arg1 = PG_GETARG_FLOAT4(0); + float8 arg2 = PG_GETARG_FLOAT4(1); + float4 result; result = arg1 + arg2; - CheckFloat4Val(result); - PG_RETURN_FLOAT4((float4) result); + /* + * There isn't any way to check for underflow of addition/subtraction + * because numbers near the underflow value have been already been + * to the point where we can't detect the that the two values + * were originally different, e.g. on x86, '1e-45'::float4 == + * '2e-45'::float4 == 1.4013e-45. + */ + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); + PG_RETURN_FLOAT4(result); } Datum @@ -795,11 +732,11 @@ float4mi(PG_FUNCTION_ARGS) { float4 arg1 = PG_GETARG_FLOAT4(0); float4 arg2 = PG_GETARG_FLOAT4(1); - double result; + float4 result; result = arg1 - arg2; - CheckFloat4Val(result); - PG_RETURN_FLOAT4((float4) result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); + PG_RETURN_FLOAT4(result); } Datum @@ -807,11 +744,12 @@ float4mul(PG_FUNCTION_ARGS) { float4 arg1 = PG_GETARG_FLOAT4(0); float4 arg2 = PG_GETARG_FLOAT4(1); - double result; + float4 result; result = arg1 * arg2; - CheckFloat4Val(result); - PG_RETURN_FLOAT4((float4) result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), + arg1 == 0 || arg2 == 0); + PG_RETURN_FLOAT4(result); } Datum @@ -819,7 +757,7 @@ float4div(PG_FUNCTION_ARGS) { float4 arg1 = PG_GETARG_FLOAT4(0); float4 arg2 = PG_GETARG_FLOAT4(1); - double result; + float4 result; if (arg2 == 0.0) ereport(ERROR, @@ -827,10 +765,10 @@ float4div(PG_FUNCTION_ARGS) errmsg("division by zero"))); /* Do division in float8, then check for overflow */ - result = (float8) arg1 / (float8) arg2; + result = arg1 / arg2; - CheckFloat4Val(result); - PG_RETURN_FLOAT4((float4) result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0); + PG_RETURN_FLOAT4(result); } /* @@ -848,7 +786,7 @@ float8pl(PG_FUNCTION_ARGS) result = arg1 + arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -861,7 +799,7 @@ float8mi(PG_FUNCTION_ARGS) result = arg1 - arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -874,7 +812,8 @@ float8mul(PG_FUNCTION_ARGS) result = arg1 * arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), + arg1 == 0 || arg2 == 0); PG_RETURN_FLOAT8(result); } @@ -892,7 +831,7 @@ float8div(PG_FUNCTION_ARGS) result = arg1 / arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -1142,7 +1081,7 @@ dtof(PG_FUNCTION_ARGS) { float8 num = PG_GETARG_FLOAT8(0); - CheckFloat4Val(num); + CHECKFLOATVAL((float4) num, isinf(num), num == 0); PG_RETURN_FLOAT4((float4) num); } @@ -1157,7 +1096,8 @@ dtoi4(PG_FUNCTION_ARGS) float8 num = PG_GETARG_FLOAT8(0); int32 result; - if (num < INT_MIN || num > INT_MAX) + /* 'Inf' is handled by INT_MAX */ + if (num < INT_MIN || num > INT_MAX || isnan(num)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); @@ -1174,15 +1114,13 @@ Datum dtoi2(PG_FUNCTION_ARGS) { float8 num = PG_GETARG_FLOAT8(0); - int16 result; - if (num < SHRT_MIN || num > SHRT_MAX) + if (num < SHRT_MIN || num > SHRT_MAX || isnan(num)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); - result = (int16) rint(num); - PG_RETURN_INT16(result); + PG_RETURN_INT16((int16) rint(num)); } @@ -1193,10 +1131,8 @@ Datum i4tod(PG_FUNCTION_ARGS) { int32 num = PG_GETARG_INT32(0); - float8 result; - result = num; - PG_RETURN_FLOAT8(result); + PG_RETURN_FLOAT8((float8) num); } @@ -1207,10 +1143,8 @@ Datum i2tod(PG_FUNCTION_ARGS) { int16 num = PG_GETARG_INT16(0); - float8 result; - result = num; - PG_RETURN_FLOAT8(result); + PG_RETURN_FLOAT8((float8) num); } @@ -1221,15 +1155,13 @@ Datum ftoi4(PG_FUNCTION_ARGS) { float4 num = PG_GETARG_FLOAT4(0); - int32 result; - if (num < INT_MIN || num > INT_MAX) + if (num < INT_MIN || num > INT_MAX || isnan(num)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("integer out of range"))); - result = (int32) rint(num); - PG_RETURN_INT32(result); + PG_RETURN_INT32((int32) rint(num)); } @@ -1240,29 +1172,25 @@ Datum ftoi2(PG_FUNCTION_ARGS) { float4 num = PG_GETARG_FLOAT4(0); - int16 result; - if (num < SHRT_MIN || num > SHRT_MAX) + if (num < SHRT_MIN || num > SHRT_MAX || isnan(num)) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("smallint out of range"))); - result = (int16) rint(num); - PG_RETURN_INT16(result); + PG_RETURN_INT16((int16) rint(num)); } /* - * i4tof - converts an int4 number to a float8 number + * i4tof - converts an int4 number to a float4 number */ Datum i4tof(PG_FUNCTION_ARGS) { int32 num = PG_GETARG_INT32(0); - float4 result; - result = num; - PG_RETURN_FLOAT4(result); + PG_RETURN_FLOAT4((float4) num); } @@ -1273,10 +1201,8 @@ Datum i2tof(PG_FUNCTION_ARGS) { int16 num = PG_GETARG_INT16(0); - float4 result; - result = num; - PG_RETURN_FLOAT4(result); + PG_RETURN_FLOAT4((float4) num); } @@ -1395,11 +1321,8 @@ Datum dround(PG_FUNCTION_ARGS) { float8 arg1 = PG_GETARG_FLOAT8(0); - float8 result; - - result = rint(arg1); - PG_RETURN_FLOAT8(result); + PG_RETURN_FLOAT8(rint(arg1)); } /* @@ -1485,7 +1408,7 @@ dsqrt(PG_FUNCTION_ARGS) result = sqrt(arg1); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -1500,6 +1423,7 @@ dcbrt(PG_FUNCTION_ARGS) float8 result; result = cbrt(arg1); + CHECKFLOATVAL(result, isinf(arg1), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -1530,16 +1454,12 @@ dpow(PG_FUNCTION_ARGS) */ errno = 0; result = pow(arg1, arg2); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("result is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -1555,21 +1475,16 @@ dexp(PG_FUNCTION_ARGS) /* * We must check both for errno getting set and for a NaN result, in order - * to deal with the vagaries of different platforms. Also, a zero result - * implies unreported underflow. + * to deal with the vagaries of different platforms. */ errno = 0; result = exp(arg1); - if (errno != 0 || result == 0.0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("result is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), false); PG_RETURN_FLOAT8(result); } @@ -1598,7 +1513,7 @@ dlog1(PG_FUNCTION_ARGS) result = log(arg1); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), arg1 == 1); PG_RETURN_FLOAT8(result); } @@ -1628,7 +1543,7 @@ dlog10(PG_FUNCTION_ARGS) result = log10(arg1); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), arg1 == 1); PG_RETURN_FLOAT8(result); } @@ -1644,16 +1559,12 @@ dacos(PG_FUNCTION_ARGS) errno = 0; result = acos(arg1); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result); } @@ -1669,16 +1580,12 @@ dasin(PG_FUNCTION_ARGS) errno = 0; result = asin(arg1); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result); } @@ -1694,16 +1601,12 @@ datan(PG_FUNCTION_ARGS) errno = 0; result = atan(arg1); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result); } @@ -1720,16 +1623,12 @@ datan2(PG_FUNCTION_ARGS) errno = 0; result = atan2(arg1, arg2); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -1745,16 +1644,12 @@ dcos(PG_FUNCTION_ARGS) errno = 0; result = cos(arg1); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result); } @@ -1770,17 +1665,13 @@ dcot(PG_FUNCTION_ARGS) errno = 0; result = tan(arg1); - if (errno != 0 || result == 0.0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); result = 1.0 / result; - CheckFloat8Val(result); + CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */, true); PG_RETURN_FLOAT8(result); } @@ -1796,16 +1687,12 @@ dsin(PG_FUNCTION_ARGS) errno = 0; result = sin(arg1); - if (errno != 0 -#ifdef HAVE_FINITE - || !finite(result) -#endif - ) + if (errno != 0) ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), true); PG_RETURN_FLOAT8(result); } @@ -1830,7 +1717,7 @@ dtan(PG_FUNCTION_ARGS) (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), errmsg("input is out of range"))); - CheckFloat8Val(result); + CHECKFLOATVAL(result, true /* tan(pi/2) == Inf */, true); PG_RETURN_FLOAT8(result); } @@ -1846,7 +1733,7 @@ degrees(PG_FUNCTION_ARGS) result = arg1 * (180.0 / M_PI); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -1872,7 +1759,7 @@ radians(PG_FUNCTION_ARGS) result = arg1 * (M_PI / 180.0); - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -1963,8 +1850,10 @@ float8_accum(PG_FUNCTION_ARGS) N += 1.0; sumX += newval; + CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true); sumX2 += newval * newval; - + CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true); + /* * If we're invoked by nodeAgg, we can cheat and modify our first * parameter in-place to reduce palloc overhead. Otherwise we construct a @@ -1999,25 +1888,24 @@ Datum float4_accum(PG_FUNCTION_ARGS) { ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); - float4 newval4 = PG_GETARG_FLOAT4(1); + /* do computations as float8 */ + float8 newval = PG_GETARG_FLOAT4(1); float8 *transvalues; float8 N, sumX, - sumX2, - newval; + sumX2; transvalues = check_float8_array(transarray, "float4_accum", 3); N = transvalues[0]; sumX = transvalues[1]; sumX2 = transvalues[2]; - /* Do arithmetic in float8 for best accuracy */ - newval = newval4; - N += 1.0; sumX += newval; + CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newval), true); sumX2 += newval * newval; - + CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newval), true); + /* * If we're invoked by nodeAgg, we can cheat and modify our first * parameter in-place to reduce palloc overhead. Otherwise we construct a @@ -2088,6 +1976,7 @@ float8_var_pop(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true); /* Watch out for roundoff error producing a negative numerator */ if (numerator <= 0.0) @@ -2116,6 +2005,7 @@ float8_var_samp(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true); /* Watch out for roundoff error producing a negative numerator */ if (numerator <= 0.0) @@ -2144,6 +2034,7 @@ float8_stddev_pop(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true); /* Watch out for roundoff error producing a negative numerator */ if (numerator <= 0.0) @@ -2172,6 +2063,7 @@ float8_stddev_samp(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true); /* Watch out for roundoff error producing a negative numerator */ if (numerator <= 0.0) @@ -2220,11 +2112,17 @@ float8_regr_accum(PG_FUNCTION_ARGS) N += 1.0; sumX += newvalX; + CHECKFLOATVAL(sumX, isinf(transvalues[1]) || isinf(newvalX), true); sumX2 += newvalX * newvalX; + CHECKFLOATVAL(sumX2, isinf(transvalues[2]) || isinf(newvalX), true); sumY += newvalY; + CHECKFLOATVAL(sumY, isinf(transvalues[3]) || isinf(newvalY), true); sumY2 += newvalY * newvalY; + CHECKFLOATVAL(sumY2, isinf(transvalues[4]) || isinf(newvalY), true); sumXY += newvalX * newvalY; - + CHECKFLOATVAL(sumXY, isinf(transvalues[5]) || isinf(newvalX) || + isinf(newvalY), true); + /* * If we're invoked by nodeAgg, we can cheat and modify our first * parameter in-place to reduce palloc overhead. Otherwise we construct a @@ -2282,6 +2180,7 @@ float8_regr_sxx(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numerator, isinf(sumX2) || isinf(sumX), true); /* Watch out for roundoff error producing a negative numerator */ if (numerator <= 0.0) @@ -2310,6 +2209,7 @@ float8_regr_syy(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumY2 - sumY * sumY; + CHECKFLOATVAL(numerator, isinf(sumY2) || isinf(sumY), true); /* Watch out for roundoff error producing a negative numerator */ if (numerator <= 0.0) @@ -2340,6 +2240,8 @@ float8_regr_sxy(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumXY - sumX * sumY; + CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) || + isinf(sumY), true); /* A negative result is valid here */ @@ -2406,6 +2308,8 @@ float8_covar_pop(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumXY - sumX * sumY; + CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) || + isinf(sumY), true); PG_RETURN_FLOAT8(numerator / (N * N)); } @@ -2432,6 +2336,8 @@ float8_covar_samp(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numerator = N * sumXY - sumX * sumY; + CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) || + isinf(sumY), true); PG_RETURN_FLOAT8(numerator / (N * (N - 1.0))); } @@ -2464,8 +2370,12 @@ float8_corr(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numeratorX = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true); numeratorY = N * sumY2 - sumY * sumY; + CHECKFLOATVAL(numeratorY, isinf(sumY2) || isinf(sumY), true); numeratorXY = N * sumXY - sumX * sumY; + CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) || + isinf(sumY), true); if (numeratorX <= 0 || numeratorY <= 0) PG_RETURN_NULL(); @@ -2501,8 +2411,12 @@ float8_regr_r2(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numeratorX = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true); numeratorY = N * sumY2 - sumY * sumY; + CHECKFLOATVAL(numeratorY, isinf(sumY2) || isinf(sumY), true); numeratorXY = N * sumXY - sumX * sumY; + CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) || + isinf(sumY), true); if (numeratorX <= 0) PG_RETURN_NULL(); /* per spec, horizontal line produces 1.0 */ @@ -2538,7 +2452,10 @@ float8_regr_slope(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numeratorX = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true); numeratorXY = N * sumXY - sumX * sumY; + CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) || + isinf(sumY), true); if (numeratorX <= 0) PG_RETURN_NULL(); @@ -2570,7 +2487,10 @@ float8_regr_intercept(PG_FUNCTION_ARGS) PG_RETURN_NULL(); numeratorX = N * sumX2 - sumX * sumX; + CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true); numeratorXXY = sumY * sumX2 - sumX * sumXY; + CHECKFLOATVAL(numeratorXXY, isinf(sumY) || isinf(sumX2) || + isinf(sumX) || isinf(sumXY), true); if (numeratorX <= 0) PG_RETURN_NULL(); @@ -2598,7 +2518,7 @@ float48pl(PG_FUNCTION_ARGS) float8 result; result = arg1 + arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -2610,7 +2530,7 @@ float48mi(PG_FUNCTION_ARGS) float8 result; result = arg1 - arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -2622,7 +2542,8 @@ float48mul(PG_FUNCTION_ARGS) float8 result; result = arg1 * arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), + arg1 == 0 || arg2 == 0); PG_RETURN_FLOAT8(result); } @@ -2639,7 +2560,7 @@ float48div(PG_FUNCTION_ARGS) errmsg("division by zero"))); result = arg1 / arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0); PG_RETURN_FLOAT8(result); } @@ -2658,7 +2579,7 @@ float84pl(PG_FUNCTION_ARGS) result = arg1 + arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -2671,7 +2592,7 @@ float84mi(PG_FUNCTION_ARGS) result = arg1 - arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), true); PG_RETURN_FLOAT8(result); } @@ -2684,7 +2605,8 @@ float84mul(PG_FUNCTION_ARGS) result = arg1 * arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), + arg1 == 0 || arg2 == 0); PG_RETURN_FLOAT8(result); } @@ -2702,7 +2624,7 @@ float84div(PG_FUNCTION_ARGS) result = arg1 / arg2; - CheckFloat8Val(result); + CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0); PG_RETURN_FLOAT8(result); } diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c index 3970bda83d2c2c0600073dd07a867e6a1a469e12..194b96c720828c57f3fc73ecd465b8016e1a2765 100644 --- a/src/backend/utils/adt/int.c +++ b/src/backend/utils/adt/int.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.75 2006/10/04 00:29:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.76 2007/01/02 20:00:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -1124,6 +1124,11 @@ int4mod(PG_FUNCTION_ARGS) ereport(ERROR, (errcode(ERRCODE_DIVISION_BY_ZERO), errmsg("division by zero"))); + + /* SELECT ((-2147483648)::int4) % (-1); causes a floating point exception */ + if (arg1 == INT_MIN && arg2 == -1) + PG_RETURN_INT32(0); + /* No overflow is possible */ PG_RETURN_INT32(arg1 % arg2); diff --git a/src/test/regress/expected/float4-exp-three-digits.out b/src/test/regress/expected/float4-exp-three-digits.out index a696a6d1c5148a91c3e7cacaa66b8557bbef0917..fd8d0b319e9f75ac930d28a82a4f0e3154ef10db 100644 --- a/src/test/regress/expected/float4-exp-three-digits.out +++ b/src/test/regress/expected/float4-exp-three-digits.out @@ -8,14 +8,14 @@ INSERT INTO FLOAT4_TBL(f1) VALUES (' -34.84 '); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20'); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20'); -- test for over and under flow -INSERT INTO FLOAT4_TBL(f1) VALUES ('10e40'); -ERROR: type "real" value out of range: overflow -INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e40'); -ERROR: type "real" value out of range: overflow -INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-40'); -ERROR: type "real" value out of range: underflow -INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-40'); -ERROR: type "real" value out of range: underflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70'); +ERROR: value out of range: overflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70'); +ERROR: value out of range: overflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70'); +ERROR: value out of range: underflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70'); +ERROR: value out of range: underflow -- bad input INSERT INTO FLOAT4_TBL(f1) VALUES (''); ERROR: invalid input syntax for type real: "" @@ -72,7 +72,11 @@ ERROR: invalid input syntax for type real: "NaN x" SELECT ' INFINITY x'::float4; ERROR: invalid input syntax for type real: " INFINITY x" SELECT 'Infinity'::float4 + 100.0; -ERROR: type "double precision" value out of range: overflow + ?column? +---------- + Infinity +(1 row) + SELECT 'Infinity'::float4 / 'Infinity'::float4; ?column? ---------- diff --git a/src/test/regress/expected/float4.out b/src/test/regress/expected/float4.out index b94816eaebd19ce85fd63e74bf5493febd55e359..b3bc431a156728a45d870ab55bd3acd9d512382b 100644 --- a/src/test/regress/expected/float4.out +++ b/src/test/regress/expected/float4.out @@ -8,14 +8,14 @@ INSERT INTO FLOAT4_TBL(f1) VALUES (' -34.84 '); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20'); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20'); -- test for over and under flow -INSERT INTO FLOAT4_TBL(f1) VALUES ('10e40'); -ERROR: type "real" value out of range: overflow -INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e40'); -ERROR: type "real" value out of range: overflow -INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-40'); -ERROR: type "real" value out of range: underflow -INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-40'); -ERROR: type "real" value out of range: underflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70'); +ERROR: value out of range: overflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70'); +ERROR: value out of range: overflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70'); +ERROR: value out of range: underflow +INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70'); +ERROR: value out of range: underflow -- bad input INSERT INTO FLOAT4_TBL(f1) VALUES (''); ERROR: invalid input syntax for type real: "" @@ -72,7 +72,11 @@ ERROR: invalid input syntax for type real: "NaN x" SELECT ' INFINITY x'::float4; ERROR: invalid input syntax for type real: " INFINITY x" SELECT 'Infinity'::float4 + 100.0; -ERROR: type "double precision" value out of range: overflow + ?column? +---------- + Infinity +(1 row) + SELECT 'Infinity'::float4 / 'Infinity'::float4; ?column? ---------- diff --git a/src/test/regress/expected/float8-exp-three-digits-win32.out b/src/test/regress/expected/float8-exp-three-digits-win32.out index 2bafa4b86c84d8a215f5ee6930dd26256ac72254..2aa7996200af48a23c81da98859287fff2ea9fee 100644 --- a/src/test/regress/expected/float8-exp-three-digits-win32.out +++ b/src/test/regress/expected/float8-exp-three-digits-win32.out @@ -72,7 +72,11 @@ ERROR: invalid input syntax for type double precision: "NaN x" SELECT ' INFINITY x'::float8; ERROR: invalid input syntax for type double precision: " INFINITY x" SELECT 'Infinity'::float8 + 100.0; -ERROR: type "double precision" value out of range: overflow + ?column? +---------- + Infinity +(1 row) + SELECT 'Infinity'::float8 / 'Infinity'::float8; ?column? ---------- @@ -342,15 +346,15 @@ UPDATE FLOAT8_TBL SET f1 = FLOAT8_TBL.f1 * '-1' WHERE FLOAT8_TBL.f1 > '0.0'; SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f; -ERROR: type "double precision" value out of range: overflow +ERROR: value out of range: overflow SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: overflow SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ; ERROR: cannot take logarithm of zero SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; ERROR: cannot take logarithm of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: underflow SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; ERROR: division by zero SELECT '' AS five, * FROM FLOAT8_TBL; diff --git a/src/test/regress/expected/float8-small-is-zero.out b/src/test/regress/expected/float8-small-is-zero.out index 71d9168950a91005219e1bbb12ce3e121ea90f84..83a78b299413abfca49c99cb962601f59fb88cb0 100644 --- a/src/test/regress/expected/float8-small-is-zero.out +++ b/src/test/regress/expected/float8-small-is-zero.out @@ -80,7 +80,11 @@ ERROR: invalid input syntax for type double precision: "NaN x" SELECT ' INFINITY x'::float8; ERROR: invalid input syntax for type double precision: " INFINITY x" SELECT 'Infinity'::float8 + 100.0; -ERROR: type "double precision" value out of range: overflow + ?column? +---------- + Infinity +(1 row) + SELECT 'Infinity'::float8 / 'Infinity'::float8; ?column? ---------- @@ -350,15 +354,15 @@ UPDATE FLOAT8_TBL SET f1 = FLOAT8_TBL.f1 * '-1' WHERE FLOAT8_TBL.f1 > '0.0'; SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f; -ERROR: type "double precision" value out of range: overflow +ERROR: value out of range: overflow SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: overflow SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ; ERROR: cannot take logarithm of zero SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; ERROR: cannot take logarithm of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: underflow SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; ERROR: division by zero SELECT '' AS five, * FROM FLOAT8_TBL; diff --git a/src/test/regress/expected/float8-small-is-zero_1.out b/src/test/regress/expected/float8-small-is-zero_1.out index 87db7d3d590babaa10422a6f6c203f279129786d..563b1a70b94392ff07d35b64a6de5a4830dd6ca2 100644 --- a/src/test/regress/expected/float8-small-is-zero_1.out +++ b/src/test/regress/expected/float8-small-is-zero_1.out @@ -80,7 +80,11 @@ ERROR: invalid input syntax for type double precision: "NaN x" SELECT ' INFINITY x'::float8; ERROR: invalid input syntax for type double precision: " INFINITY x" SELECT 'Infinity'::float8 + 100.0; -ERROR: type "double precision" value out of range: overflow + ?column? +---------- + Infinity +(1 row) + SELECT 'Infinity'::float8 / 'Infinity'::float8; ?column? ---------- @@ -350,15 +354,15 @@ UPDATE FLOAT8_TBL SET f1 = FLOAT8_TBL.f1 * '-1' WHERE FLOAT8_TBL.f1 > '0.0'; SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f; -ERROR: type "double precision" value out of range: overflow +ERROR: value out of range: overflow SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: overflow SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ; ERROR: cannot take logarithm of zero SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; ERROR: cannot take logarithm of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: underflow SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; ERROR: division by zero SELECT '' AS five, * FROM FLOAT8_TBL; diff --git a/src/test/regress/expected/float8.out b/src/test/regress/expected/float8.out index 8f5b32eac05820cdaaa155ef562b1a8c2ff7d8e0..38d2cbfb41778832779256696b53c7df7f898d65 100644 --- a/src/test/regress/expected/float8.out +++ b/src/test/regress/expected/float8.out @@ -72,7 +72,11 @@ ERROR: invalid input syntax for type double precision: "NaN x" SELECT ' INFINITY x'::float8; ERROR: invalid input syntax for type double precision: " INFINITY x" SELECT 'Infinity'::float8 + 100.0; -ERROR: type "double precision" value out of range: overflow + ?column? +---------- + Infinity +(1 row) + SELECT 'Infinity'::float8 / 'Infinity'::float8; ?column? ---------- @@ -342,15 +346,15 @@ UPDATE FLOAT8_TBL SET f1 = FLOAT8_TBL.f1 * '-1' WHERE FLOAT8_TBL.f1 > '0.0'; SELECT '' AS bad, f.f1 * '1e200' from FLOAT8_TBL f; -ERROR: type "double precision" value out of range: overflow +ERROR: value out of range: overflow SELECT '' AS bad, f.f1 ^ '1e200' from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: overflow SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 = '0.0' ; ERROR: cannot take logarithm of zero SELECT '' AS bad, ln(f.f1) from FLOAT8_TBL f where f.f1 < '0.0' ; ERROR: cannot take logarithm of a negative number SELECT '' AS bad, exp(f.f1) from FLOAT8_TBL f; -ERROR: result is out of range +ERROR: value out of range: underflow SELECT '' AS bad, f.f1 / '0.0' from FLOAT8_TBL f; ERROR: division by zero SELECT '' AS five, * FROM FLOAT8_TBL; diff --git a/src/test/regress/sql/float4.sql b/src/test/regress/sql/float4.sql index a2140f547e5b3b6e561c659e9dbba32dbdce71f5..5944e2fc14902ba5bb7fb2f678a5acfc6392f39e 100644 --- a/src/test/regress/sql/float4.sql +++ b/src/test/regress/sql/float4.sql @@ -11,10 +11,10 @@ INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e+20'); INSERT INTO FLOAT4_TBL(f1) VALUES ('1.2345678901234e-20'); -- test for over and under flow -INSERT INTO FLOAT4_TBL(f1) VALUES ('10e40'); -INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e40'); -INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-40'); -INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-40'); +INSERT INTO FLOAT4_TBL(f1) VALUES ('10e70'); +INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e70'); +INSERT INTO FLOAT4_TBL(f1) VALUES ('10e-70'); +INSERT INTO FLOAT4_TBL(f1) VALUES ('-10e-70'); -- bad input INSERT INTO FLOAT4_TBL(f1) VALUES ('');