@@ -3,7 +3,7 @@ from flint.utils.typecheck cimport typecheck
33from flint.utils.conversion cimport chars_from_str
44from flint.utils.conversion cimport str_from_chars, _str_trunc
55cimport libc.stdlib
6- import math
6+ from libc. math cimport nextafter
77
88from flint.flintlib.types.flint cimport FMPZ_REF, FMPZ_TMP, FMPZ_UNKNOWN, COEFF_IS_MPZ
99from 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