Skip to content

Commit b74ba68

Browse files
committed
BF - add extra nmant check, simplify tests
Add fallback nmant test when the nmant is stated but we don't know the type. Simplify tests to use ints instead of numpys default casting to store the values. Allow for windows 32 bit case where longdouble is something similar in precision to float64
1 parent 77a79be commit b74ba68

File tree

3 files changed

+30
-15
lines changed

3 files changed

+30
-15
lines changed

nibabel/casting.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,25 @@ def flt2nmant(flt_type):
164164
return _flt_nmant[flt_type]
165165
except KeyError:
166166
pass
167-
nmant = np.finfo(flt_type).nmant
167+
fi = np.finfo(flt_type)
168+
nmant, nexp = fi.nmant, fi.nexp
168169
# Assuming the np.float type is always IEEE 64 bit
169-
if flt_type is np.float and nmant == 52:
170+
if flt_type is np.float and (nmant, nexp) == (52, 11):
170171
return 52
171172
# Now we should be testing long doubles
172173
assert flt_type is np.longdouble
173-
if nmant == 63: # 80-bit intel type
174+
if (nmant, nexp) == (63, 15): # 80-bit intel type
174175
return 63 # Not including explicit first digit
176+
# We test the declared nmant by stepping up and down. These tests assume a
177+
# binary format
178+
i_end_contig = 2**(nmant+1) # int
179+
f_end_contig = flt_type(i_end_contig)
180+
# We need as_int here because long doubles do not necessarily convert
181+
# correctly to ints with int() - see
182+
# http://projects.scipy.org/numpy/ticket/1395
183+
if as_int(f_end_contig-1) == (i_end_contig-1): # still representable
184+
if as_int(f_end_contig+1) == i_end_contig: # Rounding down
185+
return nmant
175186
raise FloatingError('Cannot be confident of nmant value for %s' % flt_type)
176187

177188

nibabel/tests/test_casting.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
import numpy as np
55

6-
from ..casting import (float_to_int, int_clippers, CastingError, int_to_float)
6+
from ..casting import (float_to_int, as_int, int_clippers, CastingError,
7+
int_to_float)
78

89
from numpy.testing import (assert_array_almost_equal, assert_array_equal)
910

@@ -80,8 +81,9 @@ def test_casting():
8081
# We're later going to test if we modify this array
8182
farr = farr_orig.copy()
8283
mn, mx = int_clippers(ft, it)
84+
imn, imx = as_int(mn), as_int(mx)
8385
iarr = float_to_int(farr, it)
84-
exp_arr = np.array([mn, mx, mn, mx, 0, 0, 11])
86+
exp_arr = [imn, imx, imn, imx, 0, 0, 11]
8587
assert_array_equal(iarr, exp_arr)
8688
iarr = float_to_int(farr, it, infmax=True)
8789
# Float16 can overflow to infs
@@ -90,14 +92,9 @@ def test_casting():
9092
if farr[1] == np.inf:
9193
exp_arr[1] = ii.max
9294
exp_arr[2] = ii.min
93-
if exp_arr.dtype.type is np.longdouble:
94-
# longdouble seems to go through float64 on assignment; if
95-
# ii.max is above float64 integer resolution we have go through
96-
# float64 to split up the number and get full precision
97-
f64 = np.float64(ii.max)
98-
exp_arr[3] = np.longdouble(f64) + np.float64(ii.max - int(f64))
99-
else:
100-
exp_arr[3] = ii.max
95+
exp_arr[3] = ii.max
96+
# Always comparing integers here, so no issues with int-float
97+
# casting in this comparison
10198
assert_array_equal(iarr, exp_arr)
10299
# Confirm input array is not modified
103100
nans = np.isnan(farr)

nibabel/tests/test_floating.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,15 @@ def test_as_int():
3636
assert_equal(as_int(-2.1, False), -2)
3737
v = np.longdouble(2**64)
3838
assert_equal(as_int(v), 2**64)
39-
# Have all long doubles got this precision? We'll see I guess
40-
assert_equal(as_int(v+2), 2**64+2)
39+
# Have all long doubles got this precision? Windows 32-bit longdouble
40+
# appears to have 52 bit precision, but we avoid that by checking for known
41+
# precisions that are less than that required
42+
try:
43+
nmant = flt2nmant(np.longdouble)
44+
except FloatingError:
45+
nmant = None # Unknown precision, test and hope
46+
if nmant is None or nmant >= 63:
47+
assert_equal(as_int(v+2), 2**64+2)
4148

4249

4350
def test_floor_exact_16():

0 commit comments

Comments
 (0)