Skip to content

Commit 4709b63

Browse files
committed
RF: use best_float instead of np.longdouble
Refactor to use float64 instead of np.longdouble on systems (?only windows) in which np.longdouble is no more capable than float64.
1 parent d07fc76 commit 4709b63

File tree

3 files changed

+31
-11
lines changed

3 files changed

+31
-11
lines changed

nibabel/arraywriters.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def __init__(self, array, out_dtype=None)
2525

2626
import numpy as np
2727

28-
from .casting import int_to_float, as_int, int_abs, type_info
28+
from .casting import (int_to_float, as_int, int_abs, type_info, floor_exact,
29+
best_float)
2930
from .volumeutils import finite_range, array_to_file
3031

3132

@@ -331,12 +332,13 @@ def _range_scale(self):
331332
out_dtype = self._out_dtype
332333
info = type_info(out_dtype)
333334
t_mn_mx = info['min'], info['max']
335+
big_float = best_float()
334336
if out_dtype.kind == 'f':
335337
# But we want maximum precision for the calculations. Casting will
336338
# not lose precision because min/max are of fp type.
337-
t_min, t_max = np.array(t_mn_mx, dtype = np.longdouble)
339+
t_min, t_max = np.array(t_mn_mx, dtype = big_float)
338340
else: # (u)int
339-
t_min, t_max = [int_to_float(v, np.longdouble) for v in t_mn_mx]
341+
t_min, t_max = [int_to_float(v, big_float) for v in t_mn_mx]
340342
if self._out_dtype.kind == 'u':
341343
if mn < 0 and mx > 0:
342344
raise WriterError('Cannot scale negative and positive '
@@ -472,27 +474,28 @@ def _range_scale(self):
472474
self.inter = mn
473475
return
474476
# Straight mx-mn can overflow.
477+
big_float = best_float() # usually longdouble except in win 32
475478
if mn.dtype.kind == 'f': # Already floats
476479
# float64 and below cast correctly to longdouble. Longdouble needs
477480
# no casting
478-
mn2mx = np.diff(np.array([mn, mx], dtype=np.longdouble))
481+
mn2mx = np.diff(np.array([mn, mx], dtype=big_float))
479482
else: # max possible (u)int range is 2**64-1 (int64, uint64)
480483
# int_to_float covers this range. On windows longdouble is the same
481484
# as double so mn2mx will be 2**64 - thus overestimating slope
482485
# slightly. Casting to int needed to allow mx-mn to be larger than
483486
# the largest (u)int value
484-
mn2mx = int_to_float(as_int(mx) - as_int(mn), np.longdouble)
487+
mn2mx = int_to_float(as_int(mx) - as_int(mn), big_float)
485488
if out_dtype.kind == 'f':
486489
# Type range, these are also floats
487490
info = type_info(out_dtype)
488491
t_mn_mx = info['min'], info['max']
489492
else:
490493
t_mn_mx = np.iinfo(out_dtype).min, np.iinfo(out_dtype).max
491-
t_mn_mx= [int_to_float(v, np.longdouble) for v in t_mn_mx]
494+
t_mn_mx= [int_to_float(v, big_float) for v in t_mn_mx]
492495
# We want maximum precision for the calculations. Casting will
493496
# not lose precision because min/max are of fp type.
494497
assert [v.dtype.kind for v in t_mn_mx] == ['f', 'f']
495-
scaled_mn2mx = np.diff(np.array(t_mn_mx, dtype = np.longdouble))
498+
scaled_mn2mx = np.diff(np.array(t_mn_mx, dtype = big_float))
496499
slope = mn2mx / scaled_mn2mx
497500
self.inter = mn - t_mn_mx[0] * slope
498501
self.slope = slope

nibabel/casting.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,17 @@ def best_float():
469469
if type_info(np.longdouble)['nmant'] > type_info(np.float64)['nmant']:
470470
return np.longdouble
471471
return np.float64
472+
473+
474+
def ok_floats():
475+
""" Return floating point types sorted by precision
476+
477+
Remove longdouble if it has no higher precision than float64
478+
"""
479+
floats = sorted(np.sctypes['float'], key=lambda f : type_info(f)['nmant'])
480+
if best_float() != np.longdouble:
481+
floats.remove(np.longdouble)
482+
return floats
483+
484+
485+
OK_FLOATS = ok_floats()

nibabel/volumeutils.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import numpy as np
1717

1818
from .py3k import isfileobj, ZEROB
19-
from .casting import shared_range, type_info
19+
from .casting import (shared_range, type_info, as_int, best_float, OK_FLOATS)
2020

2121
sys_is_le = sys.byteorder == 'little'
2222
native_code = sys_is_le and '<' or '>'
@@ -1057,7 +1057,7 @@ def best_write_scale_ftype(arr, slope = 1.0, inter = 0.0, default=np.float32):
10571057
try:
10581058
return _ftype4scaled_finite(arr, slope, inter, 'write', default)
10591059
except ValueError:
1060-
return FLOAT_TYPES[-1]
1060+
return OK_FLOATS[-1]
10611061

10621062

10631063
def better_float_of(first, second, default=np.float32):
@@ -1111,14 +1111,17 @@ def _ftype4scaled_finite(tst_arr, slope, inter, direction='read',
11111111
""" Smallest float type for scaling of `tst_arr` that does not overflow
11121112
"""
11131113
assert direction in ('read', 'write')
1114-
def_ind = FLOAT_TYPES.index(default)
1114+
if not default in OK_FLOATS and default is np.longdouble:
1115+
# Omitted longdouble
1116+
return default
1117+
def_ind = OK_FLOATS.index(default)
11151118
# promote to arrays to avoid numpy scalar casting rules
11161119
tst_arr = np.atleast_1d(tst_arr)
11171120
slope = np.atleast_1d(slope)
11181121
inter = np.atleast_1d(inter)
11191122
warnings.filterwarnings('ignore', '.*overflow.*', RuntimeWarning)
11201123
try:
1121-
for ftype in FLOAT_TYPES[def_ind:]:
1124+
for ftype in OK_FLOATS[def_ind:]:
11221125
tst_trans = tst_arr.copy()
11231126
slope = slope.astype(ftype)
11241127
inter = inter.astype(ftype)

0 commit comments

Comments
 (0)