]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/float.c
pgindent run for 8.3.
[postgresql] / src / backend / utils / adt / float.c
index 5cd98605dc92aca3f07d11e3b8eff920b3ca796f..23e8947eec7e7a94b640fcfb2085c701eefda103 100644 (file)
@@ -3,12 +3,12 @@
  * 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
  */
@@ -167,11 +157,10 @@ is_infinite(double val)
 
        if (inf == 0)
                return 0;
-
-       if (val > 0)
+       else if (val > 0)
                return 1;
-
-       return -1;
+       else
+               return -1;
 }
 
 
@@ -262,10 +251,11 @@ float4in(PG_FUNCTION_ARGS)
 #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))
        {
@@ -285,7 +275,7 @@ float4in(PG_FUNCTION_ARGS)
                        endptr = num + 4;
                }
        }
-#endif /* HAVE_BUGGY_IRIX_STRTOD */
+#endif   /* HAVE_BUGGY_IRIX_STRTOD */
 
        /* skip trailing whitespace */
        while (*endptr != '\0' && isspace((unsigned char) *endptr))
@@ -454,10 +444,11 @@ float8in(PG_FUNCTION_ARGS)
 #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))
        {
@@ -477,7 +468,7 @@ float8in(PG_FUNCTION_ARGS)
                        endptr = num + 4;
                }
        }
-#endif /* HAVE_BUGGY_IRIX_STRTOD */
+#endif   /* HAVE_BUGGY_IRIX_STRTOD */
 
        /* skip trailing whitespace */
        while (*endptr != '\0' && isspace((unsigned char) *endptr))
@@ -717,12 +708,13 @@ float4pl(PG_FUNCTION_ARGS)
        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);
@@ -749,7 +741,7 @@ float4mul(PG_FUNCTION_ARGS)
 
        result = arg1 * arg2;
        CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
-                                       arg1 == 0 || arg2 == 0);
+                                 arg1 == 0 || arg2 == 0);
        PG_RETURN_FLOAT4(result);
 }
 
@@ -814,7 +806,7 @@ float8mul(PG_FUNCTION_ARGS)
        result = arg1 * arg2;
 
        CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
-                                       arg1 == 0 || arg2 == 0);
+                                 arg1 == 0 || arg2 == 0);
        PG_RETURN_FLOAT8(result);
 }
 
@@ -1207,108 +1199,6 @@ i2tof(PG_FUNCTION_ARGS)
 }
 
 
-/*
- *             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
@@ -1450,15 +1340,28 @@ dpow(PG_FUNCTION_ARGS)
                                 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);
@@ -1474,16 +1377,10 @@ dexp(PG_FUNCTION_ARGS)
        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);
@@ -1558,6 +1455,10 @@ dacos(PG_FUNCTION_ARGS)
        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)
@@ -1672,7 +1573,7 @@ dcot(PG_FUNCTION_ARGS)
                                 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);
 }
 
@@ -1709,16 +1610,12 @@ dtan(PG_FUNCTION_ARGS)
 
        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);
 }
 
@@ -1791,7 +1688,7 @@ setseed(PG_FUNCTION_ARGS)
 
        srandom((unsigned int) iseed);
 
-       PG_RETURN_INT32(iseed);
+       PG_RETURN_VOID();
 }
 
 
@@ -1854,7 +1751,7 @@ float8_accum(PG_FUNCTION_ARGS)
        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
@@ -1889,6 +1786,7 @@ Datum
 float4_accum(PG_FUNCTION_ARGS)
 {
        ArrayType  *transarray = PG_GETARG_ARRAYTYPE_P(0);
+
        /* do computations as float8 */
        float8          newval = PG_GETARG_FLOAT4(1);
        float8     *transvalues;
@@ -1906,7 +1804,7 @@ float4_accum(PG_FUNCTION_ARGS)
        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
@@ -2122,8 +2020,8 @@ float8_regr_accum(PG_FUNCTION_ARGS)
        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
@@ -2242,7 +2140,7 @@ float8_regr_sxy(PG_FUNCTION_ARGS)
 
        numerator = N * sumXY - sumX * sumY;
        CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
-                                       isinf(sumY), true);
+                                 isinf(sumY), true);
 
        /* A negative result is valid here */
 
@@ -2310,7 +2208,7 @@ float8_covar_pop(PG_FUNCTION_ARGS)
 
        numerator = N * sumXY - sumX * sumY;
        CHECKFLOATVAL(numerator, isinf(sumXY) || isinf(sumX) ||
-                                       isinf(sumY), true);
+                                 isinf(sumY), true);
 
        PG_RETURN_FLOAT8(numerator / (N * N));
 }
@@ -2338,7 +2236,7 @@ float8_covar_samp(PG_FUNCTION_ARGS)
 
        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)));
 }
@@ -2376,12 +2274,11 @@ float8_corr(PG_FUNCTION_ARGS)
        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
@@ -2417,7 +2314,7 @@ float8_regr_r2(PG_FUNCTION_ARGS)
        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 */
@@ -2456,7 +2353,7 @@ float8_regr_slope(PG_FUNCTION_ARGS)
        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();
 
@@ -2491,7 +2388,7 @@ float8_regr_intercept(PG_FUNCTION_ARGS)
        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();
 
@@ -2544,7 +2441,7 @@ float48mul(PG_FUNCTION_ARGS)
 
        result = arg1 * arg2;
        CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
-                                       arg1 == 0 || arg2 == 0);
+                                 arg1 == 0 || arg2 == 0);
        PG_RETURN_FLOAT8(result);
 }
 
@@ -2607,7 +2504,7 @@ float84mul(PG_FUNCTION_ARGS)
        result = arg1 * arg2;
 
        CHECKFLOATVAL(result, isinf(arg1) || isinf(arg2),
-                                       arg1 == 0 || arg2 == 0);
+                                 arg1 == 0 || arg2 == 0);
        PG_RETURN_FLOAT8(result);
 }
 
@@ -2749,6 +2646,88 @@ float84ge(PG_FUNCTION_ARGS)
        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