Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit06bf0dd

Browse files
committed
Upgrade src/port/rint.c to be POSIX-compliant.
The POSIX spec says that rint() rounds halfway cases to nearest even.Our substitute implementation failed to do that, rather rounding halfwaycases away from zero; and it also got some other cases (such as minuszero) wrong. This led to observable cross-platform differences, asreported in bug #12885 from Rich Schaaf; in particular, casting fromfloat to int didn't honor round-to-nearest-even on builds using rint.c.Implement something that attempts to cover all cases per spec, and addsome simple regression tests so that we'll notice if any platforms stillget this wrong.Although this is a bug fix, no back-patch, as a behavioral change inthe back branches was agreed not to be a good idea.Pedro Gimeno Fortea, reviewed by Michael Paquier and myself
1 parent2ed5b87 commit06bf0dd

File tree

8 files changed

+190
-1
lines changed

8 files changed

+190
-1
lines changed

‎src/port/rint.c

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,96 @@
33
* rint.c
44
* rint() implementation
55
*
6+
* By Pedro Gimeno Fortea, donated to the public domain
7+
*
68
* IDENTIFICATION
79
* src/port/rint.c
810
*
911
*-------------------------------------------------------------------------
1012
*/
1113
#include"c.h"
1214

15+
#include<float.h>
1316
#include<math.h>
1417

18+
/*
19+
* Round to nearest integer, with halfway cases going to the nearest even.
20+
*/
1521
double
1622
rint(doublex)
1723
{
18-
return (x >=0.0) ?floor(x+0.5) :ceil(x-0.5);
24+
doublex_orig;
25+
doubler;
26+
27+
/* Per POSIX, NaNs must be returned unchanged. */
28+
if (isnan(x))
29+
returnx;
30+
31+
if (x <=0.0)
32+
{
33+
/* Both positive and negative zero should be returned unchanged. */
34+
if (x==0.0)
35+
returnx;
36+
37+
/*
38+
* Subtracting 0.5 from a number very close to -0.5 can round to
39+
* exactly -1.0, producing incorrect results, so we take the opposite
40+
* approach: add 0.5 to the negative number, so that it goes closer to
41+
* zero (or at most to +0.5, which is dealt with next), avoiding the
42+
* precision issue.
43+
*/
44+
x_orig=x;
45+
x+=0.5;
46+
47+
/*
48+
* Be careful to return minus zero when input+0.5 >= 0, as that's what
49+
* rint() should return with negative input.
50+
*/
51+
if (x >=0.0)
52+
return-0.0;
53+
54+
/*
55+
* For very big numbers the input may have no decimals. That case is
56+
* detected by testing x+0.5 == x+1.0; if that happens, the input is
57+
* returned unchanged. This also covers the case of minus infinity.
58+
*/
59+
if (x==x_orig+1.0)
60+
returnx_orig;
61+
62+
/* Otherwise produce a rounded estimate. */
63+
r=floor(x);
64+
65+
/*
66+
* If the rounding did not produce exactly input+0.5 then we're done.
67+
*/
68+
if (r!=x)
69+
returnr;
70+
71+
/*
72+
* The original fractional part was exactly 0.5 (since
73+
* floor(input+0.5) == input+0.5). We need to round to nearest even.
74+
* Dividing input+0.5 by 2, taking the floor and multiplying by 2
75+
* yields the closest even number. This part assumes that division by
76+
* 2 is exact, which should be OK because underflow is impossible
77+
* here: x is an integer.
78+
*/
79+
returnfloor(x*0.5)*2.0;
80+
}
81+
else
82+
{
83+
/*
84+
* The positive case is similar but with signs inverted and using
85+
* ceil() instead of floor().
86+
*/
87+
x_orig=x;
88+
x-=0.5;
89+
if (x <=0.0)
90+
return0.0;
91+
if (x==x_orig-1.0)
92+
returnx_orig;
93+
r=ceil(x);
94+
if (r!=x)
95+
returnr;
96+
returnceil(x*0.5)*2.0;
97+
}
1998
}

‎src/test/regress/expected/int2.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,23 @@ SELECT (-32768)::int2 % (-1)::int2;
266266
0
267267
(1 row)
268268

269+
-- check rounding when casting from float
270+
SELECT x, x::int2 AS int2_value
271+
FROM (VALUES (-2.5::float8),
272+
(-1.5::float8),
273+
(-0.5::float8),
274+
(0.0::float8),
275+
(0.5::float8),
276+
(1.5::float8),
277+
(2.5::float8)) t(x);
278+
x | int2_value
279+
------+------------
280+
-2.5 | -2
281+
-1.5 | -2
282+
-0.5 | 0
283+
0 | 0
284+
0.5 | 0
285+
1.5 | 2
286+
2.5 | 2
287+
(7 rows)
288+

‎src/test/regress/expected/int4.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,23 @@ SELECT (-2147483648)::int4 % (-1)::int2;
363363
0
364364
(1 row)
365365

366+
-- check rounding when casting from float
367+
SELECT x, x::int4 AS int4_value
368+
FROM (VALUES (-2.5::float8),
369+
(-1.5::float8),
370+
(-0.5::float8),
371+
(0.0::float8),
372+
(0.5::float8),
373+
(1.5::float8),
374+
(2.5::float8)) t(x);
375+
x | int4_value
376+
------+------------
377+
-2.5 | -2
378+
-1.5 | -2
379+
-0.5 | 0
380+
0 | 0
381+
0.5 | 0
382+
1.5 | 2
383+
2.5 | 2
384+
(7 rows)
385+

‎src/test/regress/expected/int8-exp-three-digits.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,3 +846,23 @@ SELECT (-9223372036854775808)::int8 % (-1)::int2;
846846
0
847847
(1 row)
848848

849+
-- check rounding when casting from float
850+
SELECT x, x::int8 AS int8_value
851+
FROM (VALUES (-2.5::float8),
852+
(-1.5::float8),
853+
(-0.5::float8),
854+
(0.0::float8),
855+
(0.5::float8),
856+
(1.5::float8),
857+
(2.5::float8)) t(x);
858+
x | int8_value
859+
------+------------
860+
-2.5 | -2
861+
-1.5 | -2
862+
-0.5 | 0
863+
0 | 0
864+
0.5 | 0
865+
1.5 | 2
866+
2.5 | 2
867+
(7 rows)
868+

‎src/test/regress/expected/int8.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,3 +846,23 @@ SELECT (-9223372036854775808)::int8 % (-1)::int2;
846846
0
847847
(1 row)
848848

849+
-- check rounding when casting from float
850+
SELECT x, x::int8 AS int8_value
851+
FROM (VALUES (-2.5::float8),
852+
(-1.5::float8),
853+
(-0.5::float8),
854+
(0.0::float8),
855+
(0.5::float8),
856+
(1.5::float8),
857+
(2.5::float8)) t(x);
858+
x | int8_value
859+
------+------------
860+
-2.5 | -2
861+
-1.5 | -2
862+
-0.5 | 0
863+
0 | 0
864+
0.5 | 0
865+
1.5 | 2
866+
2.5 | 2
867+
(7 rows)
868+

‎src/test/regress/sql/int2.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,13 @@ SELECT ((-1::int2<<15)+1::int2)::text;
9292
SELECT (-32768)::int2* (-1)::int2;
9393
SELECT (-32768)::int2/ (-1)::int2;
9494
SELECT (-32768)::int2 % (-1)::int2;
95+
96+
-- check rounding when casting from float
97+
SELECT x, x::int2AS int2_value
98+
FROM (VALUES (-2.5::float8),
99+
(-1.5::float8),
100+
(-0.5::float8),
101+
(0.0::float8),
102+
(0.5::float8),
103+
(1.5::float8),
104+
(2.5::float8)) t(x);

‎src/test/regress/sql/int4.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,13 @@ SELECT (-2147483648)::int4 % (-1)::int4;
135135
SELECT (-2147483648)::int4* (-1)::int2;
136136
SELECT (-2147483648)::int4/ (-1)::int2;
137137
SELECT (-2147483648)::int4 % (-1)::int2;
138+
139+
-- check rounding when casting from float
140+
SELECT x, x::int4AS int4_value
141+
FROM (VALUES (-2.5::float8),
142+
(-1.5::float8),
143+
(-0.5::float8),
144+
(0.0::float8),
145+
(0.5::float8),
146+
(1.5::float8),
147+
(2.5::float8)) t(x);

‎src/test/regress/sql/int8.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,13 @@ SELECT (-9223372036854775808)::int8 % (-1)::int4;
205205
SELECT (-9223372036854775808)::int8* (-1)::int2;
206206
SELECT (-9223372036854775808)::int8/ (-1)::int2;
207207
SELECT (-9223372036854775808)::int8 % (-1)::int2;
208+
209+
-- check rounding when casting from float
210+
SELECT x, x::int8AS int8_value
211+
FROM (VALUES (-2.5::float8),
212+
(-1.5::float8),
213+
(-0.5::float8),
214+
(0.0::float8),
215+
(0.5::float8),
216+
(1.5::float8),
217+
(2.5::float8)) t(x);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp