Re: [BUGS] BUG #2846: inconsistent and confusing handling of - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: [BUGS] BUG #2846: inconsistent and confusing handling of
Date
Msg-id 200612271844.kBRIiVb18465@momjian.us
Whole thread Raw
Responses Re: [BUGS] BUG #2846: inconsistent and confusing handling of
Re: [BUGS] BUG #2846: inconsistent and confusing handling of underflows,
List pgsql-patches
I have made some more progress on this patch.  I have fixed the issue
with aggregates:

    test=> select avg(ff) from tt;
    ERROR:  type "double precision" value out of range: overflow

and tested the performance overhead of the new CheckFloat8Val() calls
the fix requires using:

    EXPLAIN ANALYZE SELECT AVG(x.COL)
    FROM (SELECT 323.2 AS COL FROM generate_series(1,1000000)) AS x;

and could not measure any overhead.

I also found a few more bugs, this one with float4 negation:

    test=> SELECT -('0'::float4);
     ?column?
    ----------
           -0
    (1 row)

    test=> SELECT -('0'::float8);
     ?column?
    ----------
            0
    (1 row)

and this one with casting 'Nan' to an integer:

    test=> SELECT 'Nan'::float8::int4;
        int4
    -------------
     -2147483648
    (1 row)

I have fixed these as well:

    test=> SELECT -('0'::float4);
     ?column?
    ----------
            0
    (1 row)

    test=>  SELECT 'Nan'::float8::int4;
    ERROR:  integer out of range

The only unsolved issue is the one with underflow checks.  I have added
comments explaining the problem in case someone ever figures out how to
address it.

If I don't receive further comments, I will apply the new attached patch
shortly.

---------------------------------------------------------------------------

bruce wrote:
> Roman Kononov wrote:
> >
> > The following bug has been logged online:
> >
> > Bug reference:      2846
> > Logged by:          Roman Kononov
> > Email address:      kononov195-pgsql@yahoo.com
> > PostgreSQL version: 8.2.0 and older
> > Operating system:   linux 2.6.15-27-amd64 ubuntu
> > Description:        inconsistent and confusing handling of underflows, NaNs
> > and INFs
> > Details:
>
> This is a very interesting bug report.  It seems you have done some good
> analysis of PostgreSQL and how it handles certain corner cases,
> infinity, and NaN.
>
> I have researched your findings and will show some fixes below:
>
> > Please compare the results of the simple queries.
> > ==============================================
> > test=# select ('NaN'::float4)::int2;
> >  int2
> > ------
> >     0
> > (1 row)
>
> There certainly should be an isnan() test when converting to int2
> because while float can represent NaN, int2 cannot.  The fix shows:
>
>     test=> select ('NaN'::float4)::int2;
>     ERROR:  smallint out of range
>
> > test=# select ('NaN'::float4)::int4;
> >     int4
> > -------------
> >  -2147483648
> > (1 row)
>
> Same for int4:
>
>     test=> select ('NaN'::float4)::int4;
>     ERROR:  integer out of range
>
> > test=# select ('NaN'::float4)::int8;
> > ERROR:  bigint out of range
>
> This one was correct because it uses rint() internally.
>
> > test=# select ('nan'::numeric)::int4;
> > ERROR:  cannot convert NaN to integer
> > ==============================================
> > test=# select abs('INF'::float4);
> >    abs
> > ----------
> >  Infinity
> > (1 row)
>
> Correct.
>
> > test=# select abs('INF'::float8);
> > ERROR:  type "double precision" value out of range: overflow
>
> This one was more complicated.  float4/8 operations test for
> results > FLOAT[84]_MAX.  This is because if you do this:
>
>     test=> select (1e201::float8)*(1e200::float8);
>
> the result internally is Infinity, so they check for Inf as a check for
> overflow.  The bottom line is that while the current code allows
> infinity to be entered, it does not allow the value to operate in many
> context because it is assumes Inf to be an overflow indicator.  I have
> fixed this by passing a boolean to indicate if any of the operands were
> infinity, and if so, allow an infinite result, so this now works:
>
>     test=> select abs('INF'::float8);
>        abs
>     ----------
>      Infinity
>     (1 row)
>
> > ==============================================
> > test=# select -('INF'::float4);
> >  ?column?
> > -----------
> >  -Infinity
> > (1 row)
> >
> > test=# select -('INF'::float8);
> > ERROR:  type "double precision" value out of range: overflow
>
> And this now works too:
>
>     test=> select -('INF'::float8);
>      ?column?
>     -----------
>      -Infinity
>     (1 row)
>
> > ==============================================
> > test=# select (1e-37::float4)*(1e-22::float4);
> >  ?column?
> > ----------
> >         0
> > (1 row)
>
> This one is quite complex.  For overflow, there is a range of values
> that is represented as > FLOAT8_MAX, but for values very large, the
> result becomes Inf.  The old code assumed an Inf result was an overflow,
> and threw an error, as I outlined above.  The new code does a better
> job.
>
> Now, for underflow.  For underflow, we again have a range slightly
> smaller than DBL_MIN where we can detect an underflow, and throw an
> error, but just like overflow, if the underflow is too small, the result
> becomes zero.  The bad news is that unlike Inf, zero isn't a special
> value.  With Inf, we could say if we got an infinite result from
> non-infinite arguments, we had an overflow, but for underflow, how do we
> know if zero is an underflow or just the correct result?  For
> multiplication, we could say that a zero result for non-zero arguments
> is almost certainly an underflow, but I don't see how we can handle the
> other operations as simply.
>
> I was not able to fix the underflow problems you reported.
>
> > test=# select (1e-37::float4)*(1e-2::float4);
> > ERROR:  type "real" value out of range: underflow
> > ==============================================
> > test=# select (1e-300::float8)*(1e-30::float8);
> >  ?column?
> > ----------
> >         0
> > (1 row)
> >
> > test=# select (1e-300::float8)*(1e-20::float8);
> > ERROR:  type "double precision" value out of range: underflow
> > ==============================================
> > test=# select ('INF'::float8-'INF'::float8);
> >  ?column?
> > ----------
> >       NaN
> > (1 row)
> >
> > test=# select ('INF'::float8+'INF'::float8);
> > ERROR:  type "double precision" value out of range: overflow
>
> This works fine now:
>
>     test=> select ('INF'::float8+'INF'::float8);
>      ?column?
>     ----------
>      Infinity
>     (1 row)
>
> > ==============================================
> > test=# select ('INF'::float4)::float8;
> >   float8
> > ----------
> >  Infinity
> > (1 row)
> >
> > test=# select ('INF'::float8)::float4;
> > ERROR:  type "real" value out of range: overflow
> > ==============================================
> > test=# select cbrt('INF'::float4);
> >    cbrt
> > ----------
> >  Infinity
> > (1 row)
> >
> > test=# select sqrt('INF'::float4);
> > ERROR:  type "double precision" value out of range: overflow
>
> This works fine too:
>
>     test=> select ('INF'::float8)::float4;
>       float4
>     ----------
>      Infinity
>     (1 row)
>
> > ==============================================
> > test=# select ((-32768::int8)::int2)%(-1::int2);
> >  ?column?
> > ----------
> >         0
> > (1 row)
> >
> > test=# select ((-2147483648::int8)::int4)%(-1::int4);
> > ERROR:  floating-point exception
> > DETAIL:  An invalid floating-point operation was signaled. This probably
> > means an out-of-range result or an invalid operation, such
> > as division by zero.
>
> This was an interesting case.  It turns out the value has to be INT_MIN,
> and the second value has to be -1.  The exception happens, I think,
> because the CPU does the division first before getting the remainder,
> and INT_MIN / -1 is > INT_MAX, hence the error.  I just special-cased it
> to return zero in the int4mod() code:
>
>     test=> select ((-2147483648::int8)::int4)%(-1::int4);
>      ?column?
>     ----------
>             0
>     (1 row)
>
> You can actually show the error without using int8:
>
>     test=> select ((-2147483648)::int4) % (-1);
>      ?column?
>     ----------
>             0
>     (1 row)
>
> The parentheses are required to make the value negative before the cast
> to int4.
>
> > ==============================================
> > test=# create table tt (ff float8);
> > CREATE TABLE
> > test=# insert into tt values (1e308),(1e308),(1e308);
> > INSERT 0 3
> > test=# select * from tt;
> >    ff
> > --------
> >  1e+308
> >  1e+308
> >  1e+308
> > (3 rows)
> >
> > test=# select avg(ff) from tt;
> >    avg
> > ----------
> >  Infinity
> > (1 row)
> >
> > test=# select stddev(ff) from tt;
> >  stddev
> > --------
> >     NaN
> > (1 row)
>
> I didn't study the aggregate cases.  Does someone want to look those
> over?
>
> The attached patch fixes all the items I mentioned above.

--
  Bruce Momjian   bruce@momjian.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/backend/utils/adt/float.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/float.c,v
retrieving revision 1.131
diff -c -c -r1.131 float.c
*** src/backend/utils/adt/float.c    23 Dec 2006 02:13:24 -0000    1.131
--- src/backend/utils/adt/float.c    27 Dec 2006 18:43:26 -0000
***************
*** 104,111 ****
  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);

--- 104,111 ----
  int            extra_float_digits = 0;        /* Added to DBL_DIG or FLT_DIG */


! static void CheckFloat4Val(double val, bool has_inf_args);
! static void CheckFloat8Val(double val, bool has_inf_args);
  static int    float4_cmp_internal(float4 a, float4 b);
  static int    float8_cmp_internal(float8 a, float8 b);

***************
*** 211,219 ****
   * 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")));
--- 211,220 ----
   * raise an ereport() error if it is
   */
  static void
! CheckFloat4Val(double val, bool has_inf_args)
  {
!     /* If one of the input arguments was infinity, allow an infinite result */
!     if (fabs(val) > FLOAT4_MAX && (!isinf(val) || !has_inf_args))
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("type \"real\" value out of range: overflow")));
***************
*** 230,241 ****
   * 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),
--- 231,261 ----
   * raise an ereport() error if it is
   */
  static void
! CheckFloat8Val(double val, bool has_inf_args)
  {
!     /*
!      *    Computations that slightly exceed FLOAT8_MAX are non-Infinity,
!      *    but those that greatly exceed FLOAT8_MAX become Infinity.  Therefore
!      *    it is difficult to tell if a value is really infinity or the result
!      *    of an overflow.  The solution is to use a boolean indicating if
!      *    the input arguments were infiity, meaning an infinite result is
!      *    probably not the result of an overflow.  This allows various
!      *    computations like SELECT 'Inf'::float8 + 5.
!      */
!     if (fabs(val) > FLOAT8_MAX && (!isinf(val) || !has_inf_args))
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
            errmsg("type \"double precision\" value out of range: overflow")));
+     /*
+      *    Underflow has similar issues to overflow, i.e. if a computation is
+      *    slighly smaller than FLOAT8_MIN, the result is non-zero, but if it is
+      *    much smaller than FLOAT8_MIN, the value becomes zero.  However,
+      *    unlike overflow, zero is not a special value and can be the result
+      *    of a computation, so there is no easy way to pass a boolean
+      *    indicating whether a zero result is reasonable or not.  It might
+      *    be possible for multiplication and division, but because of rounding,
+      *    such tests would probably not be reliable.
+      */
      if (val != 0.0 && fabs(val) < FLOAT8_MIN)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
***************
*** 369,376 ****
       * 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);

      PG_RETURN_FLOAT4((float4) val);
  }
--- 389,395 ----
       * if we get here, we have a legal double, still need to check to see if
       * it's a legal float4
       */
!     CheckFloat4Val(val, true /* allow Inf */);

      PG_RETURN_FLOAT4((float4) val);
  }
***************
*** 558,565 ****
               errmsg("invalid input syntax for type double precision: \"%s\"",
                      orig_num)));

!     if (!isinf(val))
!         CheckFloat8Val(val);

      PG_RETURN_FLOAT8(val);
  }
--- 577,583 ----
               errmsg("invalid input syntax for type double precision: \"%s\"",
                      orig_num)));

!     CheckFloat8Val(val, true /* allow Inf */);

      PG_RETURN_FLOAT8(val);
  }
***************
*** 652,659 ****
  float4um(PG_FUNCTION_ARGS)
  {
      float4        arg1 = PG_GETARG_FLOAT4(0);

!     PG_RETURN_FLOAT4((float4) -arg1);
  }

  Datum
--- 670,681 ----
  float4um(PG_FUNCTION_ARGS)
  {
      float4        arg1 = PG_GETARG_FLOAT4(0);
+     float4        result;
+
+     result = ((arg1 != 0) ? -(arg1) : arg1);

!     CheckFloat4Val(result, isinf(arg1));
!     PG_RETURN_FLOAT4(result);
  }

  Datum
***************
*** 705,716 ****
  float8abs(PG_FUNCTION_ARGS)
  {
      float8        arg1 = PG_GETARG_FLOAT8(0);
-     float8        result;

!     result = fabs(arg1);
!
!     CheckFloat8Val(result);
!     PG_RETURN_FLOAT8(result);
  }


--- 727,734 ----
  float8abs(PG_FUNCTION_ARGS)
  {
      float8        arg1 = PG_GETARG_FLOAT8(0);

!     PG_RETURN_FLOAT8(fabs(arg1));
  }


***************
*** 725,731 ****

      result = ((arg1 != 0) ? -(arg1) : arg1);

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 743,749 ----

      result = ((arg1 != 0) ? -(arg1) : arg1);

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 786,792 ****
      double        result;

      result = arg1 + arg2;
!     CheckFloat4Val(result);
      PG_RETURN_FLOAT4((float4) result);
  }

--- 804,810 ----
      double        result;

      result = arg1 + arg2;
!     CheckFloat4Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT4((float4) result);
  }

***************
*** 798,804 ****
      double        result;

      result = arg1 - arg2;
!     CheckFloat4Val(result);
      PG_RETURN_FLOAT4((float4) result);
  }

--- 816,822 ----
      double        result;

      result = arg1 - arg2;
!     CheckFloat4Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT4((float4) result);
  }

***************
*** 810,816 ****
      double        result;

      result = arg1 * arg2;
!     CheckFloat4Val(result);
      PG_RETURN_FLOAT4((float4) result);
  }

--- 828,834 ----
      double        result;

      result = arg1 * arg2;
!     CheckFloat4Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT4((float4) result);
  }

***************
*** 829,835 ****
      /* Do division in float8, then check for overflow */
      result = (float8) arg1 / (float8) arg2;

!     CheckFloat4Val(result);
      PG_RETURN_FLOAT4((float4) result);
  }

--- 847,853 ----
      /* Do division in float8, then check for overflow */
      result = (float8) arg1 / (float8) arg2;

!     CheckFloat4Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT4((float4) result);
  }

***************
*** 848,854 ****

      result = arg1 + arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 866,872 ----

      result = arg1 + arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 861,867 ****

      result = arg1 - arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 879,885 ----

      result = arg1 - arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 874,880 ****

      result = arg1 * arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 892,898 ----

      result = arg1 * arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 892,898 ****

      result = arg1 / arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 910,916 ----

      result = arg1 / arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1142,1148 ****
  {
      float8        num = PG_GETARG_FLOAT8(0);

!     CheckFloat4Val(num);

      PG_RETURN_FLOAT4((float4) num);
  }
--- 1160,1166 ----
  {
      float8        num = PG_GETARG_FLOAT8(0);

!     CheckFloat4Val(num, isinf(num));

      PG_RETURN_FLOAT4((float4) num);
  }
***************
*** 1157,1163 ****
      float8        num = PG_GETARG_FLOAT8(0);
      int32        result;

!     if (num < INT_MIN || num > INT_MAX)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("integer out of range")));
--- 1175,1182 ----
      float8        num = PG_GETARG_FLOAT8(0);
      int32        result;

!     /* '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")));
***************
*** 1176,1182 ****
      float8        num = PG_GETARG_FLOAT8(0);
      int16        result;

!     if (num < SHRT_MIN || num > SHRT_MAX)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("smallint out of range")));
--- 1195,1201 ----
      float8        num = PG_GETARG_FLOAT8(0);
      int16        result;

!     if (num < SHRT_MIN || num > SHRT_MAX || isnan(num))
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("smallint out of range")));
***************
*** 1223,1229 ****
      float4        num = PG_GETARG_FLOAT4(0);
      int32        result;

!     if (num < INT_MIN || num > INT_MAX)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("integer out of range")));
--- 1242,1248 ----
      float4        num = PG_GETARG_FLOAT4(0);
      int32        result;

!     if (num < INT_MIN || num > INT_MAX || isnan(num))
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("integer out of range")));
***************
*** 1242,1248 ****
      float4        num = PG_GETARG_FLOAT4(0);
      int16        result;

!     if (num < SHRT_MIN || num > SHRT_MAX)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("smallint out of range")));
--- 1261,1267 ----
      float4        num = PG_GETARG_FLOAT4(0);
      int16        result;

!     if (num < SHRT_MIN || num > SHRT_MAX || isnan(num))
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("smallint out of range")));
***************
*** 1485,1491 ****

      result = sqrt(arg1);

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1504,1510 ----

      result = sqrt(arg1);

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1500,1505 ****
--- 1519,1525 ----
      float8        result;

      result = cbrt(arg1);
+     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1539,1545 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("result is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1559,1565 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("result is out of range")));

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1569,1575 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("result is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1589,1595 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("result is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1598,1604 ****

      result = log(arg1);

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1618,1624 ----

      result = log(arg1);

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1628,1634 ****

      result = log10(arg1);

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1648,1654 ----

      result = log10(arg1);

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1653,1659 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1673,1679 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1678,1684 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1698,1704 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1703,1709 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1723,1729 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1729,1735 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1749,1755 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1754,1760 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1774,1780 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1780,1786 ****
                   errmsg("input is out of range")));

      result = 1.0 / result;
!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1800,1806 ----
                   errmsg("input is out of range")));

      result = 1.0 / result;
!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1805,1811 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1825,1831 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1830,1836 ****
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1850,1856 ----
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("input is out of range")));

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1846,1852 ****

      result = arg1 * (180.0 / M_PI);

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1866,1872 ----

      result = arg1 * (180.0 / M_PI);

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1872,1878 ****

      result = arg1 * (M_PI / 180.0);

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 1892,1898 ----

      result = arg1 * (M_PI / 180.0);

!     CheckFloat8Val(result, isinf(arg1));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 1963,1970 ****

      N += 1.0;
      sumX += newval;
      sumX2 += newval * newval;
!
      /*
       * If we're invoked by nodeAgg, we can cheat and modify our first
       * parameter in-place to reduce palloc overhead. Otherwise we construct a
--- 1983,1992 ----

      N += 1.0;
      sumX += newval;
+     CheckFloat8Val(sumX, isinf(transvalues[1]) || isinf(newval));
      sumX2 += newval * newval;
!     CheckFloat8Val(sumX2, isinf(transvalues[2]) || isinf(newval));
!
      /*
       * If we're invoked by nodeAgg, we can cheat and modify our first
       * parameter in-place to reduce palloc overhead. Otherwise we construct a
***************
*** 2016,2023 ****

      N += 1.0;
      sumX += newval;
      sumX2 += newval * newval;
!
      /*
       * If we're invoked by nodeAgg, we can cheat and modify our first
       * parameter in-place to reduce palloc overhead. Otherwise we construct a
--- 2038,2047 ----

      N += 1.0;
      sumX += newval;
+     CheckFloat4Val(sumX, isinf(transvalues[1]) || isinf(newval));
      sumX2 += newval * newval;
!     CheckFloat4Val(sumX2, isinf(transvalues[2]) || isinf(newval));
!
      /*
       * 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,2093 ****
--- 2112,2118 ----
          PG_RETURN_NULL();

      numerator = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numerator, isinf(sumX2) || isinf(sumX));

      /* Watch out for roundoff error producing a negative numerator */
      if (numerator <= 0.0)
***************
*** 2116,2121 ****
--- 2141,2147 ----
          PG_RETURN_NULL();

      numerator = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numerator, isinf(sumX2) || isinf(sumX));

      /* Watch out for roundoff error producing a negative numerator */
      if (numerator <= 0.0)
***************
*** 2144,2149 ****
--- 2170,2176 ----
          PG_RETURN_NULL();

      numerator = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numerator, isinf(sumX2) || isinf(sumX));

      /* Watch out for roundoff error producing a negative numerator */
      if (numerator <= 0.0)
***************
*** 2172,2177 ****
--- 2199,2205 ----
          PG_RETURN_NULL();

      numerator = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numerator, isinf(sumX2) || isinf(sumX));

      /* Watch out for roundoff error producing a negative numerator */
      if (numerator <= 0.0)
***************
*** 2220,2230 ****

      N += 1.0;
      sumX += newvalX;
      sumX2 += newvalX * newvalX;
      sumY += newvalY;
      sumY2 += newvalY * newvalY;
      sumXY += newvalX * newvalY;
!
      /*
       * If we're invoked by nodeAgg, we can cheat and modify our first
       * parameter in-place to reduce palloc overhead. Otherwise we construct a
--- 2248,2263 ----

      N += 1.0;
      sumX += newvalX;
+     CheckFloat8Val(sumX, isinf(transvalues[1]) || isinf(newvalX));
      sumX2 += newvalX * newvalX;
+     CheckFloat8Val(sumX2, isinf(transvalues[2]) || isinf(newvalX));
      sumY += newvalY;
+     CheckFloat8Val(sumY, isinf(transvalues[3]) || isinf(newvalY));
      sumY2 += newvalY * newvalY;
+     CheckFloat8Val(sumY2, isinf(transvalues[4]) || isinf(newvalY));
      sumXY += newvalX * newvalY;
!     CheckFloat8Val(sumXY, isinf(transvalues[5]) || isinf(newvalX) || isinf(newvalY));
!
      /*
       * 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,2287 ****
--- 2315,2321 ----
          PG_RETURN_NULL();

      numerator = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numerator, isinf(sumX2) || isinf(sumX));

      /* Watch out for roundoff error producing a negative numerator */
      if (numerator <= 0.0)
***************
*** 2310,2315 ****
--- 2344,2350 ----
          PG_RETURN_NULL();

      numerator = N * sumY2 - sumY * sumY;
+     CheckFloat8Val(numerator, isinf(sumY2) || isinf(sumY));

      /* Watch out for roundoff error producing a negative numerator */
      if (numerator <= 0.0)
***************
*** 2340,2345 ****
--- 2375,2381 ----
          PG_RETURN_NULL();

      numerator = N * sumXY - sumX * sumY;
+     CheckFloat8Val(numerator, isinf(sumXY) || isinf(sumX) || isinf(sumY));

      /* A negative result is valid here */

***************
*** 2406,2411 ****
--- 2442,2448 ----
          PG_RETURN_NULL();

      numerator = N * sumXY - sumX * sumY;
+     CheckFloat8Val(numerator, isinf(sumXY) || isinf(sumX) || isinf(sumY));

      PG_RETURN_FLOAT8(numerator / (N * N));
  }
***************
*** 2432,2437 ****
--- 2469,2475 ----
          PG_RETURN_NULL();

      numerator = N * sumXY - sumX * sumY;
+     CheckFloat8Val(numerator, isinf(sumXY) || isinf(sumX) || isinf(sumY));

      PG_RETURN_FLOAT8(numerator / (N * (N - 1.0)));
  }
***************
*** 2464,2471 ****
--- 2502,2512 ----
          PG_RETURN_NULL();

      numeratorX = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numeratorX, isinf(sumX2) || isinf(sumX));
      numeratorY = N * sumY2 - sumY * sumY;
+     CheckFloat8Val(numeratorY, isinf(sumY2) || isinf(sumY));
      numeratorXY = N * sumXY - sumX * sumY;
+     CheckFloat8Val(numeratorXY, isinf(sumXY) || isinf(sumX) || isinf(sumY));
      if (numeratorX <= 0 || numeratorY <= 0)
          PG_RETURN_NULL();

***************
*** 2501,2508 ****
--- 2542,2552 ----
          PG_RETURN_NULL();

      numeratorX = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numeratorX, isinf(sumX2) || isinf(sumX));
      numeratorY = N * sumY2 - sumY * sumY;
+     CheckFloat8Val(numeratorY, isinf(sumY2) || isinf(sumY));
      numeratorXY = N * sumXY - sumX * sumY;
+     CheckFloat8Val(numeratorXY, isinf(sumXY) || isinf(sumX) || isinf(sumY));
      if (numeratorX <= 0)
          PG_RETURN_NULL();
      /* per spec, horizontal line produces 1.0 */
***************
*** 2538,2544 ****
--- 2582,2590 ----
          PG_RETURN_NULL();

      numeratorX = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numeratorX, isinf(sumX2) || isinf(sumX));
      numeratorXY = N * sumXY - sumX * sumY;
+     CheckFloat8Val(numeratorXY, isinf(sumXY) || isinf(sumX) || isinf(sumY));
      if (numeratorX <= 0)
          PG_RETURN_NULL();

***************
*** 2570,2576 ****
--- 2616,2624 ----
          PG_RETURN_NULL();

      numeratorX = N * sumX2 - sumX * sumX;
+     CheckFloat8Val(numeratorX, isinf(sumX2) || isinf(sumX));
      numeratorXXY = sumY * sumX2 - sumX * sumXY;
+     CheckFloat8Val(numeratorXXY, isinf(sumY) || isinf(sumX2) || isinf(sumX) || isinf(sumXY));
      if (numeratorX <= 0)
          PG_RETURN_NULL();

***************
*** 2598,2604 ****
      float8        result;

      result = arg1 + arg2;
!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2646,2652 ----
      float8        result;

      result = arg1 + arg2;
!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2610,2616 ****
      float8        result;

      result = arg1 - arg2;
!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2658,2664 ----
      float8        result;

      result = arg1 - arg2;
!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2622,2628 ****
      float8        result;

      result = arg1 * arg2;
!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2670,2676 ----
      float8        result;

      result = arg1 * arg2;
!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2639,2645 ****
                   errmsg("division by zero")));

      result = arg1 / arg2;
!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2687,2693 ----
                   errmsg("division by zero")));

      result = arg1 / arg2;
!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2658,2664 ****

      result = arg1 + arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2706,2712 ----

      result = arg1 + arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2671,2677 ****

      result = arg1 - arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2719,2725 ----

      result = arg1 - arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2684,2690 ****

      result = arg1 * arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2732,2738 ----

      result = arg1 * arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

***************
*** 2702,2708 ****

      result = arg1 / arg2;

!     CheckFloat8Val(result);
      PG_RETURN_FLOAT8(result);
  }

--- 2750,2756 ----

      result = arg1 / arg2;

!     CheckFloat8Val(result, isinf(arg1) || isinf(arg2));
      PG_RETURN_FLOAT8(result);
  }

Index: src/backend/utils/adt/int.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/int.c,v
retrieving revision 1.75
diff -c -c -r1.75 int.c
*** src/backend/utils/adt/int.c    4 Oct 2006 00:29:59 -0000    1.75
--- src/backend/utils/adt/int.c    27 Dec 2006 18:43:26 -0000
***************
*** 1124,1129 ****
--- 1124,1134 ----
          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);
Index: src/test/regress/expected/float4.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/float4.out,v
retrieving revision 1.13
diff -c -c -r1.13 float4.out
*** src/test/regress/expected/float4.out    7 Apr 2005 01:51:40 -0000    1.13
--- src/test/regress/expected/float4.out    27 Dec 2006 18:43:28 -0000
***************
*** 72,78 ****
  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
  SELECT 'Infinity'::float4 / 'Infinity'::float4;
   ?column?
  ----------
--- 72,82 ----
  SELECT ' INFINITY    x'::float4;
  ERROR:  invalid input syntax for type real: " INFINITY    x"
  SELECT 'Infinity'::float4 + 100.0;
!  ?column?
! ----------
!  Infinity
! (1 row)
!
  SELECT 'Infinity'::float4 / 'Infinity'::float4;
   ?column?
  ----------
Index: src/test/regress/expected/float8.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/float8.out,v
retrieving revision 1.24
diff -c -c -r1.24 float8.out
*** src/test/regress/expected/float8.out    8 Jun 2005 21:15:29 -0000    1.24
--- src/test/regress/expected/float8.out    27 Dec 2006 18:43:28 -0000
***************
*** 72,78 ****
  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
  SELECT 'Infinity'::float8 / 'Infinity'::float8;
   ?column?
  ----------
--- 72,82 ----
  SELECT ' INFINITY    x'::float8;
  ERROR:  invalid input syntax for type double precision: " INFINITY    x"
  SELECT 'Infinity'::float8 + 100.0;
!  ?column?
! ----------
!  Infinity
! (1 row)
!
  SELECT 'Infinity'::float8 / 'Infinity'::float8;
   ?column?
  ----------

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Patch(es) to expose n_live_tuples and
Next
From: Tom Lane
Date:
Subject: Re: [BUGS] BUG #2846: inconsistent and confusing handling of