Re: unsafe floats

Поиск
Список
Период
Сортировка
От Neil Conway
Тема Re: unsafe floats
Дата
Msg-id 871xnzm529.fsf@mailbox.samurai.com
обсуждение исходный текст
Ответ на Re: unsafe floats  (Dennis Bjorklund <db@zigo.dhs.org>)
Ответы Re: unsafe floats  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
Dennis Bjorklund <db@zigo.dhs.org> writes:
> In C one can set a signal handler to catch floating point exceptions
> (SIGFPE). Without a handler you can get NaN and Infinity as the
> result of mathematical operations.

Okay, I think this would be a reasonable set of behavior:

    - define a new GUC var that controls how exceptional floating
      point values (NaN, inf, -inf) are handled.

    - by default, we still raise an error when a floating point
      operation results in NaN / inf / etc.; if the GUC var is toggled
      from its default value, no error is raised. This preserves
      backward compatibility with applications that expect floating
      point overflow to be reported, for example.

    - that also means that, by default, we should reject 'NaN',
      'Infinity', and '-Infinity' as input to float4/float8: if these
      values are illegal as the result of FP operations by default, it
      seems only logical to disallow them as input to the FP types.

Does that sound ok?

Unfortunately, I have more important things that I need to get wrapped
up in time for 7.5, so I can't finish this now. Dennis, would you like
to implement this?

Finally, I've attached a revised patch for 'Infinity' input to float4
and float8: this avoids breaking the regression tests by only allowing
'Infinity' and '-Infinity' as input, not as a legal result of FP
operations. This is obviously incomplete, as discussed above, but it
might be a good starting point. Should I commit this?

-Neil

Index: doc/src/sgml/syntax.sgml
===================================================================
RCS file: /var/lib/cvs/pgsql-server/doc/src/sgml/syntax.sgml,v
retrieving revision 1.89
diff -c -r1.89 syntax.sgml
*** a/doc/src/sgml/syntax.sgml    29 Nov 2003 19:51:37 -0000    1.89
--- b/doc/src/sgml/syntax.sgml    11 Mar 2004 21:18:57 -0000
***************
*** 359,364 ****
--- 359,382 ----
  </literallayout>
      </para>

+      <para>
+       In addition, there are several special constant values that are
+       accepted as numeric constants. The <type>float4</type> and
+       <type>float8</type> types allow the following special constants:
+ <literallayout>
+ Infinity
+ -Infinity
+ NaN
+ </literallayout>
+       These represnt the special values <quote>infinity</quote>,
+       <quote>negative infinity</quote>, and
+       <quote>not-a-number</quote>, respectively. The
+       <type>numeric</type> type only allows <literal>NaN</>, and the
+       integral types do not allow any of these constants. These
+       constants are treated without sensitivity to case. These values
+       should be enclosed in single quotes.
+      </para>
+
      <para>
       <indexterm><primary>integer</primary></indexterm>
       <indexterm><primary>bigint</primary></indexterm>
Index: src/backend/utils/adt/float.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/utils/adt/float.c,v
retrieving revision 1.98
diff -c -r1.98 float.c
*** a/src/backend/utils/adt/float.c    11 Mar 2004 02:11:13 -0000    1.98
--- b/src/backend/utils/adt/float.c    11 Mar 2004 20:53:15 -0000
***************
*** 114,134 ****


  /*
!  * check to see if a float4 val is outside of
!  * the FLOAT4_MIN, FLOAT4_MAX bounds.
   *
!  * raise an ereport warning if it is
! */
  static void
  CheckFloat4Val(double val)
  {
-     /*
-      * defining unsafe floats's will make float4 and float8 ops faster at
-      * the cost of safety, of course!
-      */
- #ifdef UNSAFE_FLOATS
-     return;
- #else
      if (fabs(val) > FLOAT4_MAX)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
--- 114,127 ----


  /*
!  * 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),
***************
*** 137,163 ****
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("type \"real\" value out of range: underflow")));
-
-     return;
- #endif   /* UNSAFE_FLOATS */
  }

  /*
!  * 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)
  {
-     /*
-      * defining unsafe floats's will make float4 and float8 ops faster at
-      * the cost of safety, of course!
-      */
- #ifdef UNSAFE_FLOATS
-     return;
- #else
      if (fabs(val) > FLOAT8_MAX)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
--- 130,146 ----
          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),
***************
*** 166,172 ****
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                   errmsg("type \"double precision\" value out of range: underflow")));
- #endif   /* UNSAFE_FLOATS */
  }

  /*
--- 149,154 ----
***************
*** 201,210 ****
           * empty strings, but emit a warning noting that the feature
           * is deprecated. In 7.6+, the warning should be replaced by
           * an error.
-          *
-          * XXX we should accept "Infinity" and "-Infinity" too, but
-          * what are the correct values to assign?  HUGE_VAL will
-          * provoke an error from CheckFloat4Val.
           */
          if (*num == '\0')
          {
--- 183,188 ----
***************
*** 217,222 ****
--- 195,204 ----
          }
          else if (strcasecmp(num, "NaN") == 0)
              val = NAN;
+         else if (strcasecmp(num, "Infinity") == 0)
+             val = HUGE_VAL;
+         else if (strcasecmp(num, "-Infinity") == 0)
+             val = -HUGE_VAL;
          else
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
***************
*** 239,245 ****
       * if we get here, we have a legal double, still need to check to see
       * if it's a legal float
       */
!     CheckFloat4Val(val);

      PG_RETURN_FLOAT4((float4) val);
  }
--- 221,228 ----
       * if we get here, we have a legal double, still need to check to see
       * if it's a legal float
       */
!     if (!isinf(val))
!         CheckFloat4Val(val);

      PG_RETURN_FLOAT4((float4) val);
  }
***************
*** 364,370 ****
                   errmsg("invalid input syntax for type double precision: \"%s\"",
                          num)));

!     CheckFloat8Val(val);

      PG_RETURN_FLOAT8(val);
  }
--- 347,354 ----
                   errmsg("invalid input syntax for type double precision: \"%s\"",
                          num)));

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

      PG_RETURN_FLOAT8(val);
  }
Index: src/include/pg_config_manual.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/include/pg_config_manual.h,v
retrieving revision 1.10
diff -c -r1.10 pg_config_manual.h
*** a/src/include/pg_config_manual.h    11 Feb 2004 22:55:26 -0000    1.10
--- b/src/include/pg_config_manual.h    11 Mar 2004 20:51:46 -0000
***************
*** 176,187 ****
  #define DEFAULT_PGSOCKET_DIR  "/tmp"

  /*
-  * Defining this will make float4 and float8 operations faster by
-  * suppressing overflow/underflow checks.
-  */
- /* #define UNSAFE_FLOATS */
-
- /*
   * The random() function is expected to yield values between 0 and
   * MAX_RANDOM_VALUE.  Currently, all known implementations yield
   * 0..2^31-1, so we just hardwire this constant.  We could do a
--- 176,181 ----
Index: src/test/regress/expected/float4.out
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/expected/float4.out,v
retrieving revision 1.10
diff -c -r1.10 float4.out
*** a/src/test/regress/expected/float4.out    11 Mar 2004 02:11:13 -0000    1.10
--- b/src/test/regress/expected/float4.out    11 Mar 2004 21:04:58 -0000
***************
*** 50,58 ****
--- 50,88 ----
      NaN
  (1 row)

+ SELECT 'infinity'::float4;
+   float4
+ ----------
+  Infinity
+ (1 row)
+
+ SELECT '          -INFINiTY   '::float4;
+   float4
+ -----------
+  -Infinity
+ (1 row)
+
  -- bad special inputs
  SELECT 'N A N'::float4;
  ERROR:  invalid input syntax for type real: "N A N"
+ SELECT 'NaN x'::float4;
+ 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
+ SELECT 'Infinity'::float4 / 'Infinity'::float4;
+  ?column?
+ ----------
+       NaN
+ (1 row)
+
+ SELECT 'nan'::float4 / 'nan'::float4;
+  ?column?
+ ----------
+       NaN
+ (1 row)
+
  SELECT '' AS five, FLOAT4_TBL.*;
   five |     f1
  ------+-------------
Index: src/test/regress/expected/float8.out
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/expected/float8.out,v
retrieving revision 1.18
diff -c -r1.18 float8.out
*** a/src/test/regress/expected/float8.out    11 Mar 2004 02:11:13 -0000    1.18
--- b/src/test/regress/expected/float8.out    11 Mar 2004 21:04:56 -0000
***************
*** 50,58 ****
--- 50,88 ----
      NaN
  (1 row)

+ SELECT 'infinity'::float8;
+   float8
+ ----------
+  Infinity
+ (1 row)
+
+ SELECT '          -INFINiTY   '::float8;
+   float8
+ -----------
+  -Infinity
+ (1 row)
+
  -- bad special inputs
  SELECT 'N A N'::float8;
  ERROR:  invalid input syntax for type double precision: "N A N"
+ SELECT 'NaN x'::float8;
+ 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
+ SELECT 'Infinity'::float8 / 'Infinity'::float8;
+  ?column?
+ ----------
+       NaN
+ (1 row)
+
+ SELECT 'nan'::float8 / 'nan'::float8;
+  ?column?
+ ----------
+       NaN
+ (1 row)
+
  SELECT '' AS five, FLOAT8_TBL.*;
   five |          f1
  ------+----------------------
Index: src/test/regress/sql/float4.sql
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/sql/float4.sql,v
retrieving revision 1.5
diff -c -r1.5 float4.sql
*** a/src/test/regress/sql/float4.sql    11 Mar 2004 02:11:13 -0000    1.5
--- b/src/test/regress/sql/float4.sql    11 Mar 2004 20:51:46 -0000
***************
*** 29,36 ****
--- 29,45 ----
  SELECT 'NaN'::float4;
  SELECT 'nan'::float4;
  SELECT '   NAN  '::float4;
+ SELECT 'infinity'::float4;
+ SELECT '          -INFINiTY   '::float4;
  -- bad special inputs
  SELECT 'N A N'::float4;
+ SELECT 'NaN x'::float4;
+ SELECT ' INFINITY    x'::float4;
+
+ SELECT 'Infinity'::float4 + 100.0;
+ SELECT 'Infinity'::float4 / 'Infinity'::float4;
+ SELECT 'nan'::float4 / 'nan'::float4;
+

  SELECT '' AS five, FLOAT4_TBL.*;

Index: src/test/regress/sql/float8.sql
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/sql/float8.sql,v
retrieving revision 1.9
diff -c -r1.9 float8.sql
*** a/src/test/regress/sql/float8.sql    11 Mar 2004 02:11:13 -0000    1.9
--- b/src/test/regress/sql/float8.sql    11 Mar 2004 20:51:46 -0000
***************
*** 29,36 ****
--- 29,44 ----
  SELECT 'NaN'::float8;
  SELECT 'nan'::float8;
  SELECT '   NAN  '::float8;
+ SELECT 'infinity'::float8;
+ SELECT '          -INFINiTY   '::float8;
  -- bad special inputs
  SELECT 'N A N'::float8;
+ SELECT 'NaN x'::float8;
+ SELECT ' INFINITY    x'::float8;
+
+ SELECT 'Infinity'::float8 + 100.0;
+ SELECT 'Infinity'::float8 / 'Infinity'::float8;
+ SELECT 'nan'::float8 / 'nan'::float8;

  SELECT '' AS five, FLOAT8_TBL.*;


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Josh Berkus
Дата:
Сообщение: Re: Default Stats Revisited
Следующее
От: Marty Scholes
Дата:
Сообщение: Performance and WAL on big inserts/updates