1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-15 19:21:59 +03:00

Fix float-to-integer coercions to handle edge cases correctly.

ftoi4 and its sibling coercion functions did their overflow checks in
a way that looked superficially plausible, but actually depended on an
assumption that the MIN and MAX comparison constants can be represented
exactly in the float4 or float8 domain.  That fails in ftoi4, ftoi8,
and dtoi8, resulting in a possibility that values near the MAX limit will
be wrongly converted (to negative values) when they need to be rejected.

Also, because we compared before rounding off the fractional part,
the other three functions threw errors for values that really ought
to get rounded to the min or max integer value.

Fix by doing rint() first (requiring an assumption that it handles
NaN and Inf correctly; but dtoi8 and ftoi8 were assuming that already),
and by comparing to values that should coerce to float exactly, namely
INTxx_MIN and -INTxx_MIN.  Also remove some random cosmetic discrepancies
between these six functions.

Per bug #15519 from Victor Petrovykh.  This should get back-patched,
but first let's see what the buildfarm thinks of it --- I'm not too
sure about portability of some of the regression test cases.

Patch by me; thanks to Andrew Gierth for analysis and discussion.

Discussion: https://postgr.es/m/15519-4fc785b483201ff1@postgresql.org
This commit is contained in:
Tom Lane
2018-11-23 20:57:11 -05:00
parent eb6f29141b
commit cbdb8b4c01
7 changed files with 277 additions and 29 deletions

View File

@ -257,3 +257,52 @@ SELECT '' AS five, * FROM FLOAT4_TBL;
| -1.23457e-20
(5 rows)
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
int2
-------
32767
(1 row)
SELECT '32767.6'::float4::int2;
ERROR: smallint out of range
SELECT '-32768.4'::float4::int2;
int2
--------
-32768
(1 row)
SELECT '-32768.6'::float4::int2;
ERROR: smallint out of range
SELECT '2147483520'::float4::int4;
int4
------------
2147483520
(1 row)
SELECT '2147483647'::float4::int4;
ERROR: integer out of range
SELECT '-2147483648.5'::float4::int4;
int4
-------------
-2147483648
(1 row)
SELECT '-2147483900'::float4::int4;
ERROR: integer out of range
SELECT '9223369837831520256'::float4::int8;
int8
---------------------
9223369837831520256
(1 row)
SELECT '9223372036854775807'::float4::int8;
ERROR: bigint out of range
SELECT '-9223372036854775808.5'::float4::int8;
int8
----------------------
-9223372036854775808
(1 row)
SELECT '-9223380000000000000'::float4::int8;
ERROR: bigint out of range

View File

@ -478,6 +478,55 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
| -1.2345678901234e-200
(5 rows)
-- test edge-case coercions to integer
SELECT '32767.4'::float8::int2;
int2
-------
32767
(1 row)
SELECT '32767.6'::float8::int2;
ERROR: smallint out of range
SELECT '-32768.4'::float8::int2;
int2
--------
-32768
(1 row)
SELECT '-32768.6'::float8::int2;
ERROR: smallint out of range
SELECT '2147483647.4'::float8::int4;
int4
------------
2147483647
(1 row)
SELECT '2147483647.6'::float8::int4;
ERROR: integer out of range
SELECT '-2147483648.4'::float8::int4;
int4
-------------
-2147483648
(1 row)
SELECT '-2147483648.6'::float8::int4;
ERROR: integer out of range
SELECT '9223372036854774784'::float8::int8;
int8
---------------------
9223372036854774784
(1 row)
SELECT '9223372036854775807'::float8::int8;
ERROR: bigint out of range
SELECT '-9223372036854775808.5'::float8::int8;
int8
----------------------
-9223372036854775808
(1 row)
SELECT '-9223372036854780000'::float8::int8;
ERROR: bigint out of range
-- test exact cases for trigonometric functions in degrees
SET extra_float_digits = 3;
SELECT x,

View File

@ -480,6 +480,55 @@ SELECT '' AS five, * FROM FLOAT8_TBL;
| -1.2345678901234e-200
(5 rows)
-- test edge-case coercions to integer
SELECT '32767.4'::float8::int2;
int2
-------
32767
(1 row)
SELECT '32767.6'::float8::int2;
ERROR: smallint out of range
SELECT '-32768.4'::float8::int2;
int2
--------
-32768
(1 row)
SELECT '-32768.6'::float8::int2;
ERROR: smallint out of range
SELECT '2147483647.4'::float8::int4;
int4
------------
2147483647
(1 row)
SELECT '2147483647.6'::float8::int4;
ERROR: integer out of range
SELECT '-2147483648.4'::float8::int4;
int4
-------------
-2147483648
(1 row)
SELECT '-2147483648.6'::float8::int4;
ERROR: integer out of range
SELECT '9223372036854774784'::float8::int8;
int8
---------------------
9223372036854774784
(1 row)
SELECT '9223372036854775807'::float8::int8;
ERROR: bigint out of range
SELECT '-9223372036854775808.5'::float8::int8;
int8
----------------------
-9223372036854775808
(1 row)
SELECT '-9223372036854780000'::float8::int8;
ERROR: bigint out of range
-- test exact cases for trigonometric functions in degrees
SET extra_float_digits = 3;
SELECT x,

View File

@ -81,3 +81,17 @@ UPDATE FLOAT4_TBL
WHERE FLOAT4_TBL.f1 > '0.0';
SELECT '' AS five, * FROM FLOAT4_TBL;
-- test edge-case coercions to integer
SELECT '32767.4'::float4::int2;
SELECT '32767.6'::float4::int2;
SELECT '-32768.4'::float4::int2;
SELECT '-32768.6'::float4::int2;
SELECT '2147483520'::float4::int4;
SELECT '2147483647'::float4::int4;
SELECT '-2147483648.5'::float4::int4;
SELECT '-2147483900'::float4::int4;
SELECT '9223369837831520256'::float4::int8;
SELECT '9223372036854775807'::float4::int8;
SELECT '-9223372036854775808.5'::float4::int8;
SELECT '-9223380000000000000'::float4::int8;

View File

@ -174,6 +174,20 @@ INSERT INTO FLOAT8_TBL(f1) VALUES ('-1.2345678901234e-200');
SELECT '' AS five, * FROM FLOAT8_TBL;
-- test edge-case coercions to integer
SELECT '32767.4'::float8::int2;
SELECT '32767.6'::float8::int2;
SELECT '-32768.4'::float8::int2;
SELECT '-32768.6'::float8::int2;
SELECT '2147483647.4'::float8::int4;
SELECT '2147483647.6'::float8::int4;
SELECT '-2147483648.4'::float8::int4;
SELECT '-2147483648.6'::float8::int4;
SELECT '9223372036854774784'::float8::int8;
SELECT '9223372036854775807'::float8::int8;
SELECT '-9223372036854775808.5'::float8::int8;
SELECT '-9223372036854780000'::float8::int8;
-- test exact cases for trigonometric functions in degrees
SET extra_float_digits = 3;