Re: Regression failure for floats - Mailing list pgsql-hackers
From | Neil Conway |
---|---|
Subject | Re: Regression failure for floats |
Date | |
Msg-id | 87hdwsf2u1.fsf@mailbox.samurai.com Whole thread Raw |
In response to | Regression failure for floats (Bruce Momjian <pgman@candle.pha.pa.us>) |
Responses |
Re: Regression failure for floats
|
List | 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 by date: