Re: Regression failure for floats

Поиск
Список
Период
Сортировка
От Neil Conway
Тема Re: Regression failure for floats
Дата
Msg-id 87hdwsf2u1.fsf@mailbox.samurai.com
обсуждение исходный текст
Ответ на Regression failure for floats  (Bruce Momjian <pgman@candle.pha.pa.us>)
Ответы Re: Regression failure for floats  (Neil Conway <neilc@samurai.com>)
Список pgsql-hackers
Bruce Momjian <pgman@candle.pha.pa.us> writes:
> I am seeing the following regression failure for current CVS.  On my
> OS, BSD/OS 4.3, it seems once you hit Infinity, you can't negate it.

Actually, I suspect the problem is that isinf() on your platform
returns 1 for any infinity (rather than -1 for negative infinity and 1
for positive infinity). Some existing code in float4out() and
float8out() assumed that a positive return from isinf() indicated a
positive infinity, which is not per C99.

Anyway, Tom and I worked through this issue, and a couple other
portability problems with the recent float changes, via private
email. The current patch is attached -- Tom hasn't yet gotten back to
me on whether this fixes the problem for him on HPUX, but it fixes my
OS X box.

-Neil

Index: src/backend/utils/adt/float.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/utils/adt/float.c,v
retrieving revision 1.99
diff -c -r1.99 float.c
*** a/src/backend/utils/adt/float.c    12 Mar 2004 00:25:40 -0000    1.99
--- b/src/backend/utils/adt/float.c    14 Mar 2004 01:07:21 -0000
***************
*** 109,117 ****
--- 109,138 ----

  static void CheckFloat4Val(double val);
  static void CheckFloat8Val(double val);
+ static int    is_infinite(double val);
  static int    float4_cmp_internal(float4 a, float4 b);
  static int    float8_cmp_internal(float8 a, float8 b);

+ /*
+  * Returns -1 if 'val' represents negative infinity, 1 if 'val'
+  * represents (positive) infinity, and 0 otherwise. On same platforms,
+  * this is equivalent to the isinf() macro, but not everywhere: C99
+  * does not specify that isinf() needs to distinguish between positive
+  * and negative infinity.
+  */
+ static int
+ is_infinite(double val)
+ {
+     int inf = isinf(val);
+
+     if (inf == 0)
+         return 0;
+
+     if (val > 0)
+         return 1;
+
+     return -1;
+ }

  /*
   * check to see if a float4 val is outside of the FLOAT4_MIN,
***************
*** 154,160 ****
  /*
   *        float4in        - converts "num" to float
   *                          restricted syntax:
!  *                          {<sp>} [+|-] {digit} [.{digit}] [<exp>]
   *                          where <sp> is a space, digit is 0-9,
   *                          <exp> is "e" or "E" followed by an integer.
   */
--- 175,182 ----
  /*
   *        float4in        - converts "num" to float
   *                          restricted syntax:
!  *
!   {<sp>} [+|-] {digit} [.{digit}] [<exp>]
   *                          where <sp> is a space, digit is 0-9,
   *                          <exp> is "e" or "E" followed by an integer.
   */
***************
*** 162,209 ****
  float4in(PG_FUNCTION_ARGS)
  {
      char       *num = PG_GETARG_CSTRING(0);
      double        val;
      char       *endptr;

      errno = 0;
      val = strtod(num, &endptr);

      if (errno == ERANGE)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
!                  errmsg("\"%s\" is out of range for type real", num)));

      if (num == endptr)
      {
          /*
!          * We didn't find anything that looks like a float in the input
!          *
!          * In releases prior to 7.5, we accepted an empty string as
!          * valid input (yielding a float8 of 0). In 7.5, we accept
!          * empty strings, but emit a warning noting that the feature
!          * is deprecated. In 7.6+, the warning should be replaced by
!          * an error.
           */
!         if (*num == '\0')
          {
-             ereport(WARNING,
-                     (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
-                      errmsg("deprecated input syntax for type real: \"\""),
-                      errdetail("This input will be rejected in "
-                                "a future release of PostgreSQL.")));
-             Assert(val == 0.0);
-         }
-         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),
                       errmsg("invalid input syntax for type real: \"%s\"",
!                             num)));
      }

      /* skip trailing whitespace */
--- 184,261 ----
  float4in(PG_FUNCTION_ARGS)
  {
      char       *num = PG_GETARG_CSTRING(0);
+     char       *orig_num;
      double        val;
      char       *endptr;

+     /*
+      * endptr points to the first character _after_ the sequence we
+      * recognized as a valid floating point number. orig_num points to
+      * the original input string.
+      */
+     orig_num = num;
+
+     /*
+      * Check for an empty-string input to begin with, to avoid
+      * the vagaries of strtod() on different platforms.
+      *
+      * In releases prior to 7.5, we accepted an empty string as valid
+      * input (yielding a float4 of 0). In 7.5, we accept empty
+      * strings, but emit a warning noting that the feature is
+      * deprecated. In 7.6+, the warning should be replaced by an
+      * error.
+      */
+     if (*num == '\0')
+     {
+         ereport(WARNING,
+                 (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
+                  errmsg("deprecated input syntax for type real: \"\""),
+                  errdetail("This input will be rejected in "
+                            "a future release of PostgreSQL.")));
+         PG_RETURN_FLOAT4((float4) 0.0);
+     }
+
+     /* skip leading whitespace */
+     while (*num != '\0' && isspace(*num))
+         num++;
+
      errno = 0;
      val = strtod(num, &endptr);

      if (errno == ERANGE)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
!                  errmsg("\"%s\" is out of range for type real",
!                         orig_num)));

+     /* did we not see anything that looks like a double? */
      if (num == endptr)
      {
          /*
!          * C99 requires that strtod() accept NaN and [-]Infinity, but
!          * not all platforms support that yet. Therefore, we check for
!          * these inputs ourselves.
           */
!         if (strncasecmp(num, "NaN", 3) == 0)
          {
              val = NAN;
!             endptr = num + 3;
!         }
!         else if (strncasecmp(num, "Infinity", 8) == 0)
!         {
              val = HUGE_VAL;
!             endptr = num + 8;
!         }
!         else if (strncasecmp(num, "-Infinity", 9) == 0)
!         {
              val = -HUGE_VAL;
+             endptr = num + 9;
+         }
          else
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                       errmsg("invalid input syntax for type real: \"%s\"",
!                             orig_num)));
      }

      /* skip trailing whitespace */
***************
*** 215,225 ****
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                   errmsg("invalid input syntax for type real: \"%s\"",
!                         num)));

      /*
       * 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);
--- 267,277 ----
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                   errmsg("invalid input syntax for type real: \"%s\"",
!                         orig_num)));

      /*
       * 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);
***************
*** 236,257 ****
  {
      float4        num = PG_GETARG_FLOAT4(0);
      char       *ascii = (char *) palloc(MAXFLOATWIDTH + 1);
-     int            infflag;
-     int            ndig;

      if (isnan(num))
          PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
-     infflag = isinf(num);
-     if (infflag > 0)
-         PG_RETURN_CSTRING(strcpy(ascii, "Infinity"));
-     if (infflag < 0)
-         PG_RETURN_CSTRING(strcpy(ascii, "-Infinity"));
-
-     ndig = FLT_DIG + extra_float_digits;
-     if (ndig < 1)
-         ndig = 1;

!     sprintf(ascii, "%.*g", ndig, num);

      PG_RETURN_CSTRING(ascii);
  }
--- 288,314 ----
  {
      float4        num = PG_GETARG_FLOAT4(0);
      char       *ascii = (char *) palloc(MAXFLOATWIDTH + 1);

      if (isnan(num))
          PG_RETURN_CSTRING(strcpy(ascii, "NaN"));

!     switch (is_infinite(num))
!     {
!         case 1:
!             strcpy(ascii, "Infinity");
!             break;
!         case -1:
!             strcpy(ascii, "-Infinity");
!             break;
!         default:
!         {
!             int ndig = FLT_DIG + extra_float_digits;
!             if (ndig < 1)
!                 ndig = 1;
!
!             sprintf(ascii, "%.*g", ndig, num);
!         }
!     }

      PG_RETURN_CSTRING(ascii);
  }
***************
*** 292,339 ****
  float8in(PG_FUNCTION_ARGS)
  {
      char       *num = PG_GETARG_CSTRING(0);
      double        val;
      char       *endptr;

      errno = 0;
      val = strtod(num, &endptr);

      if (errno == ERANGE)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
!                  errmsg("\"%s\" is out of range for type double precision", num)));

      if (num == endptr)
      {
          /*
!          * We didn't find anything that looks like a float in the input
!          *
!          * In releases prior to 7.5, we accepted an empty string as
!          * valid input (yielding a float8 of 0). In 7.5, we accept
!          * empty strings, but emit a warning noting that the feature
!          * is deprecated. In 7.6+, the warning should be replaced by
!          * an error.
           */
!         if (*num == '\0')
          {
-             ereport(WARNING,
-                     (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
-                      errmsg("deprecated input syntax for type double precision: \"\""),
-                      errdetail("This input will be rejected in "
-                                "a future release of PostgreSQL.")));
-             Assert(val == 0.0);
-         }
-         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),
                       errmsg("invalid input syntax for type double precision: \"%s\"",
!                             num)));
      }

      /* skip trailing whitespace */
--- 349,426 ----
  float8in(PG_FUNCTION_ARGS)
  {
      char       *num = PG_GETARG_CSTRING(0);
+     char       *orig_num;
      double        val;
      char       *endptr;

+     /*
+      * endptr points to the first character _after_ the sequence we
+      * recognized as a valid floating point number. orig_num points to
+      * the original input string.
+      */
+     orig_num = num;
+
+     /*
+      * Check for an empty-string input to begin with, to avoid
+      * the vagaries of strtod() on different platforms.
+      *
+      * In releases prior to 7.5, we accepted an empty string as valid
+      * input (yielding a float8 of 0). In 7.5, we accept empty
+      * strings, but emit a warning noting that the feature is
+      * deprecated. In 7.6+, the warning should be replaced by an
+      * error.
+      */
+     if (*num == '\0')
+     {
+         ereport(WARNING,
+                 (errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
+                  errmsg("deprecated input syntax for type double precision: \"\""),
+                  errdetail("This input will be rejected in "
+                            "a future release of PostgreSQL.")));
+         PG_RETURN_FLOAT8(0.0);
+     }
+
+     /* skip leading whitespace */
+     while (*num != '\0' && isspace(*num))
+         num++;
+
      errno = 0;
      val = strtod(num, &endptr);

      if (errno == ERANGE)
          ereport(ERROR,
                  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
!                  errmsg("\"%s\" is out of range for type double precision",
!                         orig_num)));

+     /* did we not see anything that looks like a double? */
      if (num == endptr)
      {
          /*
!          * C99 requires that strtod() accept NaN and [-]Infinity, but
!          * not all platforms support that yet. Therefore, we check for
!          * these inputs ourselves.
           */
!         if (strncasecmp(num, "NaN", 3) == 0)
          {
              val = NAN;
!             endptr = num + 3;
!         }
!         else if (strncasecmp(num, "Infinity", 8) == 0)
!         {
              val = HUGE_VAL;
!             endptr = num + 8;
!         }
!         else if (strncasecmp(num, "-Infinity", 9) == 0)
!         {
              val = -HUGE_VAL;
+             endptr = num + 9;
+         }
          else
              ereport(ERROR,
                      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                       errmsg("invalid input syntax for type double precision: \"%s\"",
!                             orig_num)));
      }

      /* skip trailing whitespace */
***************
*** 345,351 ****
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                   errmsg("invalid input syntax for type double precision: \"%s\"",
!                         num)));

      if (!isinf(val))
          CheckFloat8Val(val);
--- 432,438 ----
          ereport(ERROR,
                  (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
                   errmsg("invalid input syntax for type double precision: \"%s\"",
!                         orig_num)));

      if (!isinf(val))
          CheckFloat8Val(val);
***************
*** 362,383 ****
  {
      float8        num = PG_GETARG_FLOAT8(0);
      char       *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);
-     int            infflag;
-     int            ndig;

      if (isnan(num))
          PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
-     infflag = isinf(num);
-     if (infflag > 0)
-         PG_RETURN_CSTRING(strcpy(ascii, "Infinity"));
-     if (infflag < 0)
-         PG_RETURN_CSTRING(strcpy(ascii, "-Infinity"));
-
-     ndig = DBL_DIG + extra_float_digits;
-     if (ndig < 1)
-         ndig = 1;

!     sprintf(ascii, "%.*g", ndig, num);

      PG_RETURN_CSTRING(ascii);
  }
--- 449,475 ----
  {
      float8        num = PG_GETARG_FLOAT8(0);
      char       *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);

      if (isnan(num))
          PG_RETURN_CSTRING(strcpy(ascii, "NaN"));

!     switch (is_infinite(num))
!     {
!         case 1:
!             strcpy(ascii, "Infinity");
!             break;
!         case -1:
!             strcpy(ascii, "-Infinity");
!             break;
!         default:
!         {
!             int ndig = DBL_DIG + extra_float_digits;
!             if (ndig < 1)
!                 ndig = 1;
!
!             sprintf(ascii, "%.*g", ndig, num);
!         }
!     }

      PG_RETURN_CSTRING(ascii);
  }

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

Предыдущее
От: Jan Wieck
Дата:
Сообщение: Re: [pgsql-hackers-win32] What's left?
Следующее
От: Bruce Momjian
Дата:
Сообщение: Re: [pgsql-hackers-win32] What's left?