@@ -3,7 +3,7 @@ from flint.utils.typecheck cimport typecheck
3
3
from flint.utils.conversion cimport chars_from_str
4
4
from flint.utils.conversion cimport str_from_chars, _str_trunc
5
5
cimport libc.stdlib
6
- import math
6
+ from libc. math cimport nextafter
7
7
8
8
from flint.flintlib.types.flint cimport FMPZ_REF, FMPZ_TMP, FMPZ_UNKNOWN, COEFF_IS_MPZ
9
9
from flint.flintlib.functions.flint cimport flint_free
@@ -106,15 +106,37 @@ cdef class fmpz(flint_scalar):
106
106
return fmpz_get_intlong(self .val)
107
107
108
108
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 :
110
115
# 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))
118
140
119
141
def __floor__ (self ):
120
142
return self
0 commit comments