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);
}