* float.c
* Functions for the built-in floating-point types.
*
- * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.133 2007/01/02 20:50:35 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.152 2007/11/15 21:14:39 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <float.h>
#include <math.h>
#include <limits.h>
-/* for finite() on Solaris */
-#ifdef HAVE_IEEEFP_H
-#include <ieeefp.h>
-#endif
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#define M_PI 3.14159265358979323846
#endif
-/* Recent HPUXen have isfinite() macro in place of more standard finite() */
-#if !defined(HAVE_FINITE) && defined(isfinite)
-#define finite(x) isfinite(x)
-#define HAVE_FINITE 1
-#endif
-
/* Visual C++ etc lacks NAN, and won't accept 0.0/0.0. NAN definition from
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrfNotNumberNANItems.asp
*/
if (inf == 0)
return 0;
-
- if (val > 0)
+ else if (val > 0)
return 1;
-
- return -1;
+ else
+ return -1;
}
#endif /* HAVE_BUGGY_SOLARIS_STRTOD */
#ifdef HAVE_BUGGY_IRIX_STRTOD
+
/*
- * In some IRIX versions, strtod() recognizes only "inf", so if the
- * input is "infinity" we have to skip over "inity". Also, it may
- * return positive infinity for "-inf".
+ * In some IRIX versions, strtod() recognizes only "inf", so if the input
+ * is "infinity" we have to skip over "inity". Also, it may return
+ * positive infinity for "-inf".
*/
if (isinf(val))
{
endptr = num + 4;
}
}
-#endif /* HAVE_BUGGY_IRIX_STRTOD */
+#endif /* HAVE_BUGGY_IRIX_STRTOD */
/* skip trailing whitespace */
while (*endptr != '\0' && isspace((unsigned char) *endptr))
#endif /* HAVE_BUGGY_SOLARIS_STRTOD */
#ifdef HAVE_BUGGY_IRIX_STRTOD
+
/*
- * In some IRIX versions, strtod() recognizes only "inf", so if the
- * input is "infinity" we have to skip over "inity". Also, it may
- * return positive infinity for "-inf".
+ * In some IRIX versions, strtod() recognizes only "inf", so if the input
+ * is "infinity" we have to skip over "inity". Also, it may return
+ * positive infinity for "-inf".
*/
if (isinf(val))
{
endptr = num + 4;
}
}
-#endif /* HAVE_BUGGY_IRIX_STRTOD */
+#endif /* HAVE_BUGGY_IRIX_STRTOD */
/* skip trailing whitespace */
while (*endptr != '\0' && isspace((unsigned char) *endptr))
float4 result;
result = arg1 + arg2;
+
/*
- * 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.
+ * 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);
result = arg1 * arg2;
CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT4(result);
}
result = arg1 * arg2;
CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
}
-/*
- * float8_text - converts a float8 number to a text string
- */
-Datum
-float8_text(PG_FUNCTION_ARGS)
-{
- float8 num = PG_GETARG_FLOAT8(0);
- text *result;
- int len;
- char *str;
-
- str = DatumGetCString(DirectFunctionCall1(float8out,
- Float8GetDatum(num)));
-
- len = strlen(str) + VARHDRSZ;
-
- result = (text *) palloc(len);
-
- VARATT_SIZEP(result) = len;
- memcpy(VARDATA(result), str, (len - VARHDRSZ));
-
- pfree(str);
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/*
- * text_float8 - converts a text string to a float8 number
- */
-Datum
-text_float8(PG_FUNCTION_ARGS)
-{
- text *string = PG_GETARG_TEXT_P(0);
- Datum result;
- int len;
- char *str;
-
- len = (VARSIZE(string) - VARHDRSZ);
- str = palloc(len + 1);
- memcpy(str, VARDATA(string), len);
- *(str + len) = '\0';
-
- result = DirectFunctionCall1(float8in, CStringGetDatum(str));
-
- pfree(str);
-
- PG_RETURN_DATUM(result);
-}
-
-
-/*
- * float4_text - converts a float4 number to a text string
- */
-Datum
-float4_text(PG_FUNCTION_ARGS)
-{
- float4 num = PG_GETARG_FLOAT4(0);
- text *result;
- int len;
- char *str;
-
- str = DatumGetCString(DirectFunctionCall1(float4out,
- Float4GetDatum(num)));
-
- len = strlen(str) + VARHDRSZ;
-
- result = (text *) palloc(len);
-
- VARATT_SIZEP(result) = len;
- memcpy(VARDATA(result), str, (len - VARHDRSZ));
-
- pfree(str);
-
- PG_RETURN_TEXT_P(result);
-}
-
-
-/*
- * text_float4 - converts a text string to a float4 number
- */
-Datum
-text_float4(PG_FUNCTION_ARGS)
-{
- text *string = PG_GETARG_TEXT_P(0);
- Datum result;
- int len;
- char *str;
-
- len = (VARSIZE(string) - VARHDRSZ);
- str = palloc(len + 1);
- memcpy(str, VARDATA(string), len);
- *(str + len) = '\0';
-
- result = DirectFunctionCall1(float4in, CStringGetDatum(str));
-
- pfree(str);
-
- PG_RETURN_DATUM(result);
-}
-
-
/*
* =======================
* RANDOM FLOAT8 OPERATORS
errmsg("invalid argument for power function")));
/*
- * We must check both for errno getting set and for a NaN result, in order
- * to deal with the vagaries of different platforms...
+ * pow() sets errno only on some platforms, depending on whether it
+ * follows _IEEE_, _POSIX_, _XOPEN_, or _SVID_, so we try to avoid using
+ * errno. However, some platform/CPU combinations return errno == EDOM
+ * and result == Nan for negative arg1 and very large arg2 (they must be
+ * using something different from our floor() test to decide it's
+ * invalid). Other platforms (HPPA) return errno == ERANGE and a large
+ * (HUGE_VAL) but finite result to signal overflow.
*/
errno = 0;
result = pow(arg1, arg2);
- if (errno != 0)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("result is out of range")));
+ if (errno == EDOM && isnan(result))
+ {
+ if ((fabs(arg1) > 1 && arg2 >= 0) || (fabs(arg1) < 1 && arg2 < 0))
+ /* 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();
CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2), arg1 == 0);
PG_RETURN_FLOAT8(result);
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
- /*
- * We must check both for errno getting set and for a NaN result, in order
- * to deal with the vagaries of different platforms.
- */
errno = 0;
result = exp(arg1);
- if (errno != 0)
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("result is out of range")));
+ if (errno == ERANGE && result != 0 && !isinf(result))
+ result = get_float8_infinity();
CHECKFLOATVAL(result, isinf(arg1), false);
PG_RETURN_FLOAT8(result);
float8 arg1 = PG_GETARG_FLOAT8(0);
float8 result;
+ /*
+ * We use errno here because the trigonometric functions are cyclic and
+ * hard to check for underflow.
+ */
errno = 0;
result = acos(arg1);
if (errno != 0)
errmsg("input is out of range")));
result = 1.0 / result;
- CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */, true);
+ CHECKFLOATVAL(result, true /* cotan(pi/2) == inf */ , true);
PG_RETURN_FLOAT8(result);
}
errno = 0;
result = tan(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")));
- CHECKFLOATVAL(result, true /* tan(pi/2) == Inf */, true);
+ CHECKFLOATVAL(result, true /* tan(pi/2) == Inf */ , true);
PG_RETURN_FLOAT8(result);
}
srandom((unsigned int) iseed);
- PG_RETURN_INT32(iseed);
+ PG_RETURN_VOID();
}
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
float4_accum(PG_FUNCTION_ARGS)
{
ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0);
+
/* do computations as float8 */
float8 newval = PG_GETARG_FLOAT4(1);
float8 *transvalues;
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
CHECKFLOATVAL(sumY2, isinf(transvalues[4]) || isinf(newvalY), true);
sumXY += newvalX * newvalY;
CHECKFLOATVAL(sumXY, isinf(transvalues[5]) || isinf(newvalX) ||
- isinf(newvalY), true);
-
+ 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
numerator = N * sumXY - sumX * sumY;
CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ isinf(sumY), true);
/* A negative result is valid here */
numerator = N * sumXY - sumX * sumY;
CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ isinf(sumY), true);
PG_RETURN_FLOAT8(numerator / (N * N));
}
numerator = N * sumXY - sumX * sumY;
CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ isinf(sumY), true);
PG_RETURN_FLOAT8(numerator / (N * (N - 1.0)));
}
CHECKFLOATVAL(numeratorY, isinf(sumY2) || isinf(sumY), true);
numeratorXY = N * sumXY - sumX * sumY;
CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ isinf(sumY), true);
if (numeratorX <= 0 || numeratorY <= 0)
PG_RETURN_NULL();
- PG_RETURN_FLOAT8(sqrt((numeratorXY * numeratorXY) /
- (numeratorX * numeratorY)));
+ PG_RETURN_FLOAT8(numeratorXY / sqrt(numeratorX * numeratorY));
}
Datum
CHECKFLOATVAL(numeratorY, isinf(sumY2) || isinf(sumY), true);
numeratorXY = N * sumXY - sumX * sumY;
CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ isinf(sumY), true);
if (numeratorX <= 0)
PG_RETURN_NULL();
/* per spec, horizontal line produces 1.0 */
CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true);
numeratorXY = N * sumXY - sumX * sumY;
CHECKFLOATVAL(numeratorXY, isinf(sumXY) || isinf(sumX) ||
- isinf(sumY), true);
+ isinf(sumY), true);
if (numeratorX <= 0)
PG_RETURN_NULL();
CHECKFLOATVAL(numeratorX, isinf(sumX2) || isinf(sumX), true);
numeratorXXY = sumY * sumX2 - sumX * sumXY;
CHECKFLOATVAL(numeratorXXY, isinf(sumY) || isinf(sumX2) ||
- isinf(sumX) || isinf(sumXY), true);
+ isinf(sumX) || isinf(sumXY), true);
if (numeratorX <= 0)
PG_RETURN_NULL();
result = arg1 * arg2;
CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
result = arg1 * arg2;
CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
- arg1 == 0 || arg2 == 0);
+ arg1 == 0 || arg2 == 0);
PG_RETURN_FLOAT8(result);
}
PG_RETURN_BOOL(float8_cmp_internal(arg1, arg2) >= 0);
}
+/*
+ * Implements the float8 version of the width_bucket() function
+ * defined by SQL2003. See also width_bucket_numeric().
+ *
+ * 'bound1' and 'bound2' are the lower and upper bounds of the
+ * histogram's range, respectively. 'count' is the number of buckets
+ * in the histogram. width_bucket() returns an integer indicating the
+ * bucket number that 'operand' belongs to in an equiwidth histogram
+ * with the specified characteristics. An operand smaller than the
+ * lower bound is assigned to bucket 0. An operand greater than the
+ * upper bound is assigned to an additional bucket (with number
+ * count+1). We don't allow "NaN" for any of the float8 inputs, and we
+ * don't allow either of the histogram bounds to be +/- infinity.
+ */
+Datum
+width_bucket_float8(PG_FUNCTION_ARGS)
+{
+ float8 operand = PG_GETARG_FLOAT8(0);
+ float8 bound1 = PG_GETARG_FLOAT8(1);
+ float8 bound2 = PG_GETARG_FLOAT8(2);
+ int32 count = PG_GETARG_INT32(3);
+ int32 result;
+
+ if (count <= 0.0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
+ errmsg("count must be greater than zero")));
+
+ if (isnan(operand) || isnan(bound1) || isnan(bound2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
+ errmsg("operand, lower bound and upper bound cannot be NaN")));
+
+ /* Note that we allow "operand" to be infinite */
+ if (is_infinite(bound1) || is_infinite(bound2))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
+ errmsg("lower and upper bounds must be finite")));
+
+ if (bound1 < bound2)
+ {
+ if (operand < bound1)
+ result = 0;
+ else if (operand >= bound2)
+ {
+ result = count + 1;
+ /* check for overflow */
+ if (result < count)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ }
+ else
+ result = ((float8) count * (operand - bound1) / (bound2 - bound1)) + 1;
+ }
+ else if (bound1 > bound2)
+ {
+ if (operand > bound1)
+ result = 0;
+ else if (operand <= bound2)
+ {
+ result = count + 1;
+ /* check for overflow */
+ if (result < count)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+ }
+ else
+ result = ((float8) count * (bound1 - operand) / (bound1 - bound2)) + 1;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION),
+ errmsg("lower bound cannot equal upper bound")));
+ result = 0; /* keep the compiler quiet */
+ }
+
+ PG_RETURN_INT32(result);
+}
+
/* ========== PRIVATE ROUTINES ========== */
#ifndef HAVE_CBRT