From c318aeed84438619fc6b8c647def1730a110f04b Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 1 Feb 2012 13:11:16 -0500 Subject: [PATCH] Try to be more consistent about accepting denormalized float8 numbers. On some platforms, strtod() reports ERANGE for a denormalized value (ie, one that can be represented as distinct from zero, but is too small to have full precision). On others, it doesn't. It seems better to try to accept these values consistently, so add a test to see if the result value indicates a true out-of-range condition. This should be okay per Single Unix Spec. On machines where the underlying math isn't IEEE standard, the behavior for such small numbers may not be very consistent, but then it wouldn't be anyway. Marti Raudsepp, after a proposal by Jeroen Vermeulen --- src/backend/utils/adt/float.c | 44 +++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 08434dc7ee..245030f531 100644 --- a/src/backend/utils/adt/float.c +++ b/src/backend/utils/adt/float.c @@ -217,6 +217,8 @@ float4in(PG_FUNCTION_ARGS) /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { + int save_errno = errno; + /* * C99 requires that strtod() accept NaN and [-]Infinity, but not all * platforms support that yet (and some accept them but set ERANGE @@ -237,11 +239,21 @@ float4in(PG_FUNCTION_ARGS) val = -get_float4_infinity(); endptr = num + 9; } - else if (errno == ERANGE) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("\"%s\" is out of range for type real", - orig_num))); + else if (save_errno == ERANGE) + { + /* + * Some platforms return ERANGE for denormalized numbers (those + * that are not zero, but are too close to zero to have full + * precision). We'd prefer not to throw error for that, so try + * to detect whether it's a "real" out-of-range condition by + * checking to see if the result is zero or huge. + */ + if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"%s\" is out of range for type real", + orig_num))); + } else ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), @@ -410,6 +422,8 @@ float8in(PG_FUNCTION_ARGS) /* did we not see anything that looks like a double? */ if (endptr == num || errno != 0) { + int save_errno = errno; + /* * C99 requires that strtod() accept NaN and [-]Infinity, but not all * platforms support that yet (and some accept them but set ERANGE @@ -430,11 +444,21 @@ float8in(PG_FUNCTION_ARGS) val = -get_float8_infinity(); endptr = num + 9; } - else if (errno == ERANGE) - ereport(ERROR, - (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), - errmsg("\"%s\" is out of range for type double precision", - orig_num))); + else if (save_errno == ERANGE) + { + /* + * Some platforms return ERANGE for denormalized numbers (those + * that are not zero, but are too close to zero to have full + * precision). We'd prefer not to throw error for that, so try + * to detect whether it's a "real" out-of-range condition by + * checking to see if the result is zero or huge. + */ + if (val == 0.0 || val >= HUGE_VAL || val <= -HUGE_VAL) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"%s\" is out of range for type double precision", + orig_num))); + } else ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), -- 2.40.0