Skip to content

Commit 9382a42

Browse files
Implement round-to-even compliant rounding
1 parent b3656ca commit 9382a42

File tree

1 file changed

+31
-9
lines changed

1 file changed

+31
-9
lines changed

src/flint/types/fmpz.pyx

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ from flint.utils.typecheck cimport typecheck
33
from flint.utils.conversion cimport chars_from_str
44
from flint.utils.conversion cimport str_from_chars, _str_trunc
55
cimport libc.stdlib
6-
import math
6+
from libc.math cimport nextafter
77

88
from flint.flintlib.types.flint cimport FMPZ_REF, FMPZ_TMP, FMPZ_UNKNOWN, COEFF_IS_MPZ
99
from flint.flintlib.functions.flint cimport flint_free
@@ -106,15 +106,37 @@ cdef class fmpz(flint_scalar):
106106
return fmpz_get_intlong(self.val)
107107

108108
def __float__(self):
109-
if fmpz_bits(self.val) <= 1023:
109+
cdef slong bits = fmpz_bits(self.val)
110+
cdef long tz
111+
cdef double d
112+
cdef fmpz_struct xabs[1]
113+
cdef int roundup = 0
114+
if bits <= 1023:
110115
# Known to be representable by a IEEE-754 double
111-
return fmpz_get_d(self.val)
112-
113-
cdef slong exp
114-
# fmpz_get_d_2exp is always accurate
115-
# math.ldexp handles overflow checks
116-
cdef double d = fmpz_get_d_2exp(&exp, self.val)
117-
return math.ldexp(d, exp)
116+
d = fmpz_get_d(self.val)
117+
# fmpz_get_d always rounds towards zero
118+
# Sometimes we need to round away from zero:
119+
# - if the 54-th most significant bit is 1
120+
# - if further bits are not zero
121+
# - or if all further bits are zero but the 53-th bit is 1 (round-to-even convention)
122+
tz = fmpz_val2(self.val)
123+
if fmpz_sgn(self.val) == -1:
124+
fmpz_init(xabs)
125+
fmpz_abs(xabs, self.val)
126+
if bits >= 54 and fmpz_tstbit(xabs, bits - 54) == 1 and \
127+
(tz < bits - 54 or (tz == bits - 54 and fmpz_tstbit(xabs, bits - 53) == 1)):
128+
roundup = 1
129+
fmpz_clear(xabs)
130+
else:
131+
if bits >= 54 and fmpz_tstbit(self.val, bits - 54) == 1 and \
132+
(tz < bits - 54 or (tz == bits - 54 and fmpz_tstbit(self.val, bits - 53) == 1)):
133+
roundup = 1
134+
if roundup:
135+
# increase the mantissa of d by 1
136+
d = nextafter(d, 2 * d)
137+
return d
138+
139+
return float(fmpz_get_intlong(self.val))
118140

119141
def __floor__(self):
120142
return self

0 commit comments

Comments
 (0)