Skip to content

Commit 80ac9b0

Browse files
author
Neil Conway
committed
Portability fixes and bug fixes for recent floating point input changes.
In particular, don't depend on strtod() to accept 'NaN' and 'Infinity' inputs (while this is required by C99, not all platforms are compliant with that yet). Also, don't require glibc's behavior from isinf(): it seems that on a lot of platforms isinf() does not itself distinguish between negative and positive infinity.
1 parent 7665d1b commit 80ac9b0

File tree

1 file changed

+157
-66
lines changed

1 file changed

+157
-66
lines changed

src/backend/utils/adt/float.c

+157-66
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.99 2004/03/12 00:25:40 neilc Exp $
11+
* $PostgreSQL: pgsql/src/backend/utils/adt/float.c,v 1.100 2004/03/14 05:22:52 neilc Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -109,9 +109,30 @@ int extra_float_digits = 0; /* Added to DBL_DIG or FLT_DIG */
109109

110110
static void CheckFloat4Val(double val);
111111
static void CheckFloat8Val(double val);
112+
static int is_infinite(double val);
112113
static int float4_cmp_internal(float4 a, float4 b);
113114
static int float8_cmp_internal(float8 a, float8 b);
114115

116+
/*
117+
* Returns -1 if 'val' represents negative infinity, 1 if 'val'
118+
* represents (positive) infinity, and 0 otherwise. On some platforms,
119+
* this is equivalent to the isinf() macro, but not everywhere: C99
120+
* does not specify that isinf() needs to distinguish between positive
121+
* and negative infinity.
122+
*/
123+
static int
124+
is_infinite(double val)
125+
{
126+
int inf = isinf(val);
127+
128+
if (inf == 0)
129+
return 0;
130+
131+
if (val > 0)
132+
return 1;
133+
134+
return -1;
135+
}
115136

116137
/*
117138
* check to see if a float4 val is outside of the FLOAT4_MIN,
@@ -162,48 +183,78 @@ Datum
162183
float4in(PG_FUNCTION_ARGS)
163184
{
164185
char *num = PG_GETARG_CSTRING(0);
186+
char *orig_num;
165187
double val;
166188
char *endptr;
167189

190+
/*
191+
* endptr points to the first character _after_ the sequence we
192+
* recognized as a valid floating point number. orig_num points to
193+
* the original input string.
194+
*/
195+
orig_num = num;
196+
197+
/*
198+
* Check for an empty-string input to begin with, to avoid
199+
* the vagaries of strtod() on different platforms.
200+
*
201+
* In releases prior to 7.5, we accepted an empty string as valid
202+
* input (yielding a float4 of 0). In 7.5, we accept empty
203+
* strings, but emit a warning noting that the feature is
204+
* deprecated. In 7.6+, the warning should be replaced by an
205+
* error.
206+
*/
207+
if (*num == '\0')
208+
{
209+
ereport(WARNING,
210+
(errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
211+
errmsg("deprecated input syntax for type real: \"\""),
212+
errdetail("This input will be rejected in "
213+
"a future release of PostgreSQL.")));
214+
PG_RETURN_FLOAT4((float4) 0.0);
215+
}
216+
217+
/* skip leading whitespace */
218+
while (*num != '\0' && isspace(*num))
219+
num++;
220+
168221
errno = 0;
169222
val = strtod(num, &endptr);
170223

171224
if (errno == ERANGE)
172225
ereport(ERROR,
173226
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
174-
errmsg("\"%s\" is out of range for type real", num)));
227+
errmsg("\"%s\" is out of range for type real",
228+
orig_num)));
175229

230+
/* did we not see anything that looks like a double? */
176231
if (num == endptr)
177232
{
178233
/*
179-
* We didn't find anything that looks like a float in the input
180-
*
181-
* In releases prior to 7.5, we accepted an empty string as
182-
* valid input (yielding a float8 of 0). In 7.5, we accept
183-
* empty strings, but emit a warning noting that the feature
184-
* is deprecated. In 7.6+, the warning should be replaced by
185-
* an error.
234+
* C99 requires that strtod() accept NaN and [-]Infinity, but
235+
* not all platforms support that yet. Therefore, we check for
236+
* these inputs ourselves.
186237
*/
187-
if (*num == '\0')
238+
if (strncasecmp(num, "NaN", 3) == 0)
188239
{
189-
ereport(WARNING,
190-
(errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
191-
errmsg("deprecated input syntax for type real: \"\""),
192-
errdetail("This input will be rejected in "
193-
"a future release of PostgreSQL.")));
194-
Assert(val == 0.0);
195-
}
196-
else if (strcasecmp(num, "NaN") == 0)
197240
val = NAN;
198-
else if (strcasecmp(num, "Infinity") == 0)
241+
endptr = num + 3;
242+
}
243+
else if (strncasecmp(num, "Infinity", 8) == 0)
244+
{
199245
val = HUGE_VAL;
200-
else if (strcasecmp(num, "-Infinity") == 0)
246+
endptr = num + 8;
247+
}
248+
else if (strncasecmp(num, "-Infinity", 9) == 0)
249+
{
201250
val = -HUGE_VAL;
251+
endptr = num + 9;
252+
}
202253
else
203254
ereport(ERROR,
204255
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
205256
errmsg("invalid input syntax for type real: \"%s\"",
206-
num)));
257+
orig_num)));
207258
}
208259

209260
/* skip trailing whitespace */
@@ -215,11 +266,11 @@ float4in(PG_FUNCTION_ARGS)
215266
ereport(ERROR,
216267
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
217268
errmsg("invalid input syntax for type real: \"%s\"",
218-
num)));
269+
orig_num)));
219270

220271
/*
221272
* if we get here, we have a legal double, still need to check to see
222-
* if it's a legal float
273+
* if it's a legal float4
223274
*/
224275
if (!isinf(val))
225276
CheckFloat4Val(val);
@@ -236,22 +287,27 @@ float4out(PG_FUNCTION_ARGS)
236287
{
237288
float4 num = PG_GETARG_FLOAT4(0);
238289
char *ascii = (char *) palloc(MAXFLOATWIDTH + 1);
239-
int infflag;
240-
int ndig;
241290

242291
if (isnan(num))
243292
PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
244-
infflag = isinf(num);
245-
if (infflag > 0)
246-
PG_RETURN_CSTRING(strcpy(ascii, "Infinity"));
247-
if (infflag < 0)
248-
PG_RETURN_CSTRING(strcpy(ascii, "-Infinity"));
249293

250-
ndig = FLT_DIG + extra_float_digits;
251-
if (ndig < 1)
252-
ndig = 1;
294+
switch (is_infinite(num))
295+
{
296+
case 1:
297+
strcpy(ascii, "Infinity");
298+
break;
299+
case -1:
300+
strcpy(ascii, "-Infinity");
301+
break;
302+
default:
303+
{
304+
int ndig = FLT_DIG + extra_float_digits;
305+
if (ndig < 1)
306+
ndig = 1;
253307

254-
sprintf(ascii, "%.*g", ndig, num);
308+
sprintf(ascii, "%.*g", ndig, num);
309+
}
310+
}
255311

256312
PG_RETURN_CSTRING(ascii);
257313
}
@@ -292,48 +348,78 @@ Datum
292348
float8in(PG_FUNCTION_ARGS)
293349
{
294350
char *num = PG_GETARG_CSTRING(0);
351+
char *orig_num;
295352
double val;
296353
char *endptr;
297354

355+
/*
356+
* endptr points to the first character _after_ the sequence we
357+
* recognized as a valid floating point number. orig_num points to
358+
* the original input string.
359+
*/
360+
orig_num = num;
361+
362+
/*
363+
* Check for an empty-string input to begin with, to avoid
364+
* the vagaries of strtod() on different platforms.
365+
*
366+
* In releases prior to 7.5, we accepted an empty string as valid
367+
* input (yielding a float8 of 0). In 7.5, we accept empty
368+
* strings, but emit a warning noting that the feature is
369+
* deprecated. In 7.6+, the warning should be replaced by an
370+
* error.
371+
*/
372+
if (*num == '\0')
373+
{
374+
ereport(WARNING,
375+
(errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
376+
errmsg("deprecated input syntax for type double precision: \"\""),
377+
errdetail("This input will be rejected in "
378+
"a future release of PostgreSQL.")));
379+
PG_RETURN_FLOAT8(0.0);
380+
}
381+
382+
/* skip leading whitespace */
383+
while (*num != '\0' && isspace(*num))
384+
num++;
385+
298386
errno = 0;
299387
val = strtod(num, &endptr);
300388

301389
if (errno == ERANGE)
302390
ereport(ERROR,
303391
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
304-
errmsg("\"%s\" is out of range for type double precision", num)));
392+
errmsg("\"%s\" is out of range for type double precision",
393+
orig_num)));
305394

395+
/* did we not see anything that looks like a double? */
306396
if (num == endptr)
307397
{
308398
/*
309-
* We didn't find anything that looks like a float in the input
310-
*
311-
* In releases prior to 7.5, we accepted an empty string as
312-
* valid input (yielding a float8 of 0). In 7.5, we accept
313-
* empty strings, but emit a warning noting that the feature
314-
* is deprecated. In 7.6+, the warning should be replaced by
315-
* an error.
399+
* C99 requires that strtod() accept NaN and [-]Infinity, but
400+
* not all platforms support that yet. Therefore, we check for
401+
* these inputs ourselves.
316402
*/
317-
if (*num == '\0')
403+
if (strncasecmp(num, "NaN", 3) == 0)
318404
{
319-
ereport(WARNING,
320-
(errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
321-
errmsg("deprecated input syntax for type double precision: \"\""),
322-
errdetail("This input will be rejected in "
323-
"a future release of PostgreSQL.")));
324-
Assert(val == 0.0);
325-
}
326-
else if (strcasecmp(num, "NaN") == 0)
327405
val = NAN;
328-
else if (strcasecmp(num, "Infinity") == 0)
406+
endptr = num + 3;
407+
}
408+
else if (strncasecmp(num, "Infinity", 8) == 0)
409+
{
329410
val = HUGE_VAL;
330-
else if (strcasecmp(num, "-Infinity") == 0)
411+
endptr = num + 8;
412+
}
413+
else if (strncasecmp(num, "-Infinity", 9) == 0)
414+
{
331415
val = -HUGE_VAL;
416+
endptr = num + 9;
417+
}
332418
else
333419
ereport(ERROR,
334420
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
335421
errmsg("invalid input syntax for type double precision: \"%s\"",
336-
num)));
422+
orig_num)));
337423
}
338424

339425
/* skip trailing whitespace */
@@ -345,7 +431,7 @@ float8in(PG_FUNCTION_ARGS)
345431
ereport(ERROR,
346432
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
347433
errmsg("invalid input syntax for type double precision: \"%s\"",
348-
num)));
434+
orig_num)));
349435

350436
if (!isinf(val))
351437
CheckFloat8Val(val);
@@ -362,22 +448,27 @@ float8out(PG_FUNCTION_ARGS)
362448
{
363449
float8 num = PG_GETARG_FLOAT8(0);
364450
char *ascii = (char *) palloc(MAXDOUBLEWIDTH + 1);
365-
int infflag;
366-
int ndig;
367451

368452
if (isnan(num))
369453
PG_RETURN_CSTRING(strcpy(ascii, "NaN"));
370-
infflag = isinf(num);
371-
if (infflag > 0)
372-
PG_RETURN_CSTRING(strcpy(ascii, "Infinity"));
373-
if (infflag < 0)
374-
PG_RETURN_CSTRING(strcpy(ascii, "-Infinity"));
375454

376-
ndig = DBL_DIG + extra_float_digits;
377-
if (ndig < 1)
378-
ndig = 1;
455+
switch (is_infinite(num))
456+
{
457+
case 1:
458+
strcpy(ascii, "Infinity");
459+
break;
460+
case -1:
461+
strcpy(ascii, "-Infinity");
462+
break;
463+
default:
464+
{
465+
int ndig = DBL_DIG + extra_float_digits;
466+
if (ndig < 1)
467+
ndig = 1;
379468

380-
sprintf(ascii, "%.*g", ndig, num);
469+
sprintf(ascii, "%.*g", ndig, num);
470+
}
471+
}
381472

382473
PG_RETURN_CSTRING(ascii);
383474
}

0 commit comments

Comments
 (0)