Re: unsafe floats - Mailing list pgsql-hackers
| From | Neil Conway |
|---|---|
| Subject | Re: unsafe floats |
| Date | |
| Msg-id | 87ptbjq5sc.fsf@mailbox.samurai.com Whole thread Raw |
| In response to | Re: unsafe floats (Dennis Bjorklund <db@zigo.dhs.org>) |
| Responses |
Re: unsafe floats
Re: unsafe floats |
| List | pgsql-hackers |
Dennis Bjorklund <db@zigo.dhs.org> writes:
> I would like them to produce the IEEE 754 number 'infinity' (usually
> writte 'Inf' in other languages).
Fair enough. Attached is a patch that implements this. I chose to
remove UNSAFE_FLOATS: if anyone thinks that is worth keeping, speak up
now.
Example behavior:
nconway=# select 'Infinity'::float4, '-Infinity'::float8;
float4 | float8
----------+-----------
Infinity | -Infinity
(1 row)
However, this patch causes the regression tests to fail. The reason
for that is the following change in behavior:
nconway=# select '-1.2345678901234e+200'::float8 * '1e200';
?column?
-----------
-Infinity
(1 row)
Without the patch, we bail out due to overflow:
nconway=# select '-1.2345678901234e+200'::float8 * '1e200';
ERROR: type "double precision" value out of range: overflow
The problem is that CheckFloat8Val() is used in two places: in
float8in() to check that the input fits inside a float8 (which is a
little silly, since strtod() by definition returns something that fits
inside a double), and after various float8 operations (including
multiplication).
As part of the patch, I modified CheckFloat8Val() to not reject
infinite FP values. What happens in the example above is that the C
multiplication operation performed by float8mul() returns '-Infinity'
-- prior to the patch, CheckFloat8Val() rejected that as an overflow,
but with the patch it no longer does.
So, what is the correct behavior: if you multiply two values and get a
result that exceeds the range of a float8, should you get
'Infinity'/'-Infinity', or an overflow error?
(Either policy is implementable: in the former case, we'd check for an
infinite input in float8in() but outside of CheckFloat8Val(), and in
the latter case we'd just remove the overflow checking for floating
point ops.)
Comments?
-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.98
diff -c -r1.98 float.c
*** a/src/backend/utils/adt/float.c 11 Mar 2004 02:11:13 -0000 1.98
--- b/src/backend/utils/adt/float.c 11 Mar 2004 02:43:43 -0000
***************
*** 114,134 ****
/*
! * check to see if a float4 val is outside of
! * the FLOAT4_MIN, FLOAT4_MAX bounds.
*
! * raise an ereport warning if it is
! */
static void
CheckFloat4Val(double val)
{
! /*
! * defining unsafe floats's will make float4 and float8 ops faster at
! * the cost of safety, of course!
! */
! #ifdef UNSAFE_FLOATS
! return;
! #else
if (fabs(val) > FLOAT4_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
--- 114,130 ----
/*
! * check to see if a float4 val is outside of the FLOAT4_MIN,
! * FLOAT4_MAX bounds.
*
! * raise an ereport() error if it is
! */
static void
CheckFloat4Val(double val)
{
! if (isinf(val))
! return;
!
if (fabs(val) > FLOAT4_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
***************
*** 137,163 ****
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("type \"real\" value out of range: underflow")));
-
- return;
- #endif /* UNSAFE_FLOATS */
}
/*
! * check to see if a float8 val is outside of
! * the FLOAT8_MIN, FLOAT8_MAX bounds.
*
! * raise an ereport error if it is
*/
static void
CheckFloat8Val(double val)
{
! /*
! * defining unsafe floats's will make float4 and float8 ops faster at
! * the cost of safety, of course!
! */
! #ifdef UNSAFE_FLOATS
! return;
! #else
if (fabs(val) > FLOAT8_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
--- 133,152 ----
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("type \"real\" value out of range: underflow")));
}
/*
! * check to see if a float8 val is outside of the FLOAT8_MIN,
! * FLOAT8_MAX bounds.
*
! * raise an ereport() error if it is
*/
static void
CheckFloat8Val(double val)
{
! if (isinf(val))
! return;
!
if (fabs(val) > FLOAT8_MAX)
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
***************
*** 166,172 ****
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("type \"double precision\" value out of range: underflow")));
- #endif /* UNSAFE_FLOATS */
}
/*
--- 155,160 ----
***************
*** 201,210 ****
* empty strings, but emit a warning noting that the feature
* is deprecated. In 7.6+, the warning should be replaced by
* an error.
- *
- * XXX we should accept "Infinity" and "-Infinity" too, but
- * what are the correct values to assign? HUGE_VAL will
- * provoke an error from CheckFloat4Val.
*/
if (*num == '\0')
{
--- 189,194 ----
***************
*** 217,222 ****
--- 201,210 ----
}
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),
Index: src/include/pg_config_manual.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/include/pg_config_manual.h,v
retrieving revision 1.10
diff -c -r1.10 pg_config_manual.h
*** a/src/include/pg_config_manual.h 11 Feb 2004 22:55:26 -0000 1.10
--- b/src/include/pg_config_manual.h 11 Mar 2004 04:48:08 -0000
***************
*** 176,187 ****
#define DEFAULT_PGSOCKET_DIR "/tmp"
/*
- * Defining this will make float4 and float8 operations faster by
- * suppressing overflow/underflow checks.
- */
- /* #define UNSAFE_FLOATS */
-
- /*
* The random() function is expected to yield values between 0 and
* MAX_RANDOM_VALUE. Currently, all known implementations yield
* 0..2^31-1, so we just hardwire this constant. We could do a
--- 176,181 ----
Index: src/test/regress/sql/float4.sql
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/sql/float4.sql,v
retrieving revision 1.5
diff -c -r1.5 float4.sql
*** a/src/test/regress/sql/float4.sql 11 Mar 2004 02:11:13 -0000 1.5
--- b/src/test/regress/sql/float4.sql 11 Mar 2004 04:59:32 -0000
***************
*** 29,36 ****
--- 29,45 ----
SELECT 'NaN'::float4;
SELECT 'nan'::float4;
SELECT ' NAN '::float4;
+ SELECT 'infinity'::float4;
+ SELECT ' -INFINiTY '::float4;
-- bad special inputs
SELECT 'N A N'::float4;
+ SELECT 'NaN x'::float4;
+ SELECT ' INFINITY x'::float4;
+
+ SELECT 'Infinity'::float4 + 100.0;
+ SELECT 'Infinity'::float4 / 'Infinity'::float4;
+ SELECT 'nan'::float4 / 'nan'::float4;
+
SELECT '' AS five, FLOAT4_TBL.*;
Index: src/test/regress/sql/float8.sql
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/test/regress/sql/float8.sql,v
retrieving revision 1.9
diff -c -r1.9 float8.sql
*** a/src/test/regress/sql/float8.sql 11 Mar 2004 02:11:13 -0000 1.9
--- b/src/test/regress/sql/float8.sql 11 Mar 2004 05:38:11 -0000
***************
*** 29,36 ****
--- 29,44 ----
SELECT 'NaN'::float8;
SELECT 'nan'::float8;
SELECT ' NAN '::float8;
+ SELECT 'infinity'::float8;
+ SELECT ' -INFINiTY '::float8;
-- bad special inputs
SELECT 'N A N'::float8;
+ SELECT 'NaN x'::float8;
+ SELECT ' INFINITY x'::float8;
+
+ SELECT 'Infinity'::float8 + 100.0;
+ SELECT 'Infinity'::float8 / 'Infinity'::float8;
+ SELECT 'nan'::float8 / 'nan'::float8;
SELECT '' AS five, FLOAT8_TBL.*;
pgsql-hackers by date: