Skip to content

Commit 9e0f052

Browse files
committed
RF - use type_info for min, max
Replace uses of np.finfo with type_info() function to insulate against errors in the finfo results for longdouble on PPC.
1 parent a50fc88 commit 9e0f052

File tree

9 files changed

+57
-64
lines changed

9 files changed

+57
-64
lines changed

nibabel/arraywriters.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ 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
28+
from .casting import int_to_float, as_int, int_abs, type_info
2929
from .volumeutils import finite_range, array_to_file
3030

3131

@@ -329,13 +329,13 @@ def _range_scale(self):
329329
""" Calculate scaling based on data range and output type """
330330
mn, mx = self.finite_range() # These can be floats or integers
331331
out_dtype = self._out_dtype
332+
info = type_info(out_dtype)
333+
t_mn_mx = info['min'], info['max']
332334
if out_dtype.kind == 'f':
333-
t_mn_mx = np.finfo(out_dtype).min, np.finfo(out_dtype).max
334335
# But we want maximum precision for the calculations. Casting will
335336
# not lose precision because min/max are of fp type.
336337
t_min, t_max = np.array(t_mn_mx, dtype = np.longdouble)
337338
else: # (u)int
338-
t_mn_mx = np.iinfo(out_dtype).min, np.iinfo(out_dtype).max
339339
t_min, t_max = [int_to_float(v, np.longdouble) for v in t_mn_mx]
340340
if self._out_dtype.kind == 'u':
341341
if mn < 0 and mx > 0:
@@ -484,7 +484,8 @@ def _range_scale(self):
484484
mn2mx = int_to_float(as_int(mx) - as_int(mn), np.longdouble)
485485
if out_dtype.kind == 'f':
486486
# Type range, these are also floats
487-
t_mn_mx = np.finfo(out_dtype).min, np.finfo(out_dtype).max
487+
info = type_info(out_dtype)
488+
t_mn_mx = info['min'], info['max']
488489
else:
489490
t_mn_mx = np.iinfo(out_dtype).min, np.iinfo(out_dtype).max
490491
t_mn_mx= [int_to_float(v, np.longdouble) for v in t_mn_mx]

nibabel/casting.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,14 @@ def floor_exact(val, flt_type):
363363
faval = int_to_float(aval, flt_type)
364364
except OverflowError:
365365
faval = np.inf
366+
info = type_info(flt_type)
366367
if faval == np.inf:
367-
return sign * np.finfo(flt_type).max
368+
return sign * info['max']
368369
if as_int(faval) <= aval: # as_int deals with longdouble safely
369370
# Float casting has made the value go down or stay the same
370371
return sign * faval
371372
# Float casting made the value go up
372-
nmant = type_info(flt_type)['nmant']
373-
biggest_gap = 2**(floor_log2(aval) - nmant)
373+
biggest_gap = 2**(floor_log2(aval) - info['nmant'])
374374
assert biggest_gap > 1
375375
faval -= flt_type(biggest_gap)
376376
return sign * faval

nibabel/tests/test_arraywriters.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, array, out_dtype=None, order='F')
3737
WriterError, ScalingError, ArrayWriter,
3838
make_array_writer, get_slope_inter)
3939

40-
from ..casting import int_abs
40+
from ..casting import int_abs, type_info
4141

4242
from ..volumeutils import array_from_file, apply_read_scaling
4343

@@ -354,8 +354,8 @@ def test_io_scaling():
354354
(np.float32, np.int16, None)):
355355
out_dtype = np.dtype(out_type)
356356
arr = np.zeros((3,), dtype=in_type)
357-
info = np.finfo(in_type) if arr.dtype.kind == 'f' else np.iinfo(in_type)
358-
arr[0], arr[1] = info.min, info.max
357+
info = type_info(in_type)
358+
arr[0], arr[1] = info['min'], info['max']
359359
aw = SlopeInterArrayWriter(arr, out_dtype, calc_scale=False)
360360
if not err is None:
361361
assert_raises(err, aw.calc_scale)
@@ -429,17 +429,16 @@ def test_writers_roundtrip():
429429

430430

431431
def test_to_float():
432+
start, stop = 0, 100
432433
for in_type in NUMERIC_TYPES:
433-
if in_type in IUINT_TYPES:
434-
info = np.iinfo(in_type)
435-
mn, mx, start, stop, step = info.min, info.max, 0, 100, 1
436-
else:
437-
info = np.finfo(in_type)
438-
mn, mx, start, stop, step = info.min, info.max, 0, 100, 0.5
434+
step = 1 if in_type in IUINT_TYPES else 0.5
435+
info = type_info(in_type)
436+
mn, mx = info['min'], info['max']
439437
arr = np.arange(start, stop, step, dtype=in_type)
440438
arr[0] = mn
441439
arr[-1] = mx
442440
for out_type in CFLOAT_TYPES:
441+
out_info = type_info(out_type)
443442
for klass in (SlopeInterArrayWriter, SlopeArrayWriter,
444443
ArrayWriter):
445444
if in_type in COMPLEX_TYPES and out_type in FLOAT_TYPES:
@@ -451,8 +450,7 @@ def test_to_float():
451450
arr_back = round_trip(aw)
452451
assert_array_equal(arr.astype(out_type), arr_back)
453452
# Check too-big values overflowed correctly
454-
out_min = np.finfo(out_type).min
455-
out_max = np.finfo(out_type).max
453+
out_min, out_max = out_info['min'], out_info['max']
456454
assert_true(np.all(arr_back[arr > out_max] == np.inf))
457455
assert_true(np.all(arr_back[arr < out_min] == -np.inf))
458456

@@ -498,8 +496,8 @@ def test_writer_maker():
498496
def test_float_int_min_max():
499497
# Conversion between float and int
500498
for in_dt in FLOAT_TYPES:
501-
finf = np.finfo(in_dt)
502-
arr = np.array([finf.min, finf.max], dtype=in_dt)
499+
finf = type_info(in_dt)
500+
arr = np.array([finf['min'], finf['max']], dtype=in_dt)
503501
for out_dt in IUINT_TYPES:
504502
try:
505503
aw = SlopeInterArrayWriter(arr, out_dt)

nibabel/tests/test_floating.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
if have_float16:
1919
IEEE_floats.append(np.float16)
2020

21-
LD_INFO = np.finfo(np.longdouble)
21+
LD_INFO = type_info(np.longdouble)
2222

2323

2424
def test_type_info():
@@ -63,7 +63,7 @@ def test_type_info():
6363
def test_nmant():
6464
for t in IEEE_floats:
6565
assert_equal(type_info(t)['nmant'], np.finfo(t).nmant)
66-
if (LD_INFO.nmant, LD_INFO.nexp) == (63, 15):
66+
if (LD_INFO['nmant'], LD_INFO['nexp']) == (63, 15):
6767
assert_equal(type_info(np.longdouble)['nmant'], 63)
6868

6969

@@ -87,7 +87,7 @@ def test_as_int():
8787
v = np.longdouble(2) ** (nmant + 1) - 1
8888
assert_equal(as_int(v), 2**(nmant + 1) -1)
8989
# Check for predictable overflow
90-
nexp64 = floor_log2(np.finfo(np.float64).max)
90+
nexp64 = floor_log2(type_info(np.float64)['max'])
9191
val = np.longdouble(2**nexp64) * 2 # outside float64 range
9292
assert_raises(OverflowError, as_int, val)
9393
assert_raises(OverflowError, as_int, -val)
@@ -103,7 +103,7 @@ def test_int_to_float():
103103
assert_equal(int_to_float(i, ie3), ie3(i))
104104
assert_equal(int_to_float(-i, ie3), ie3(-i))
105105
# IEEEs in this case are binary formats only
106-
nexp = floor_log2(np.finfo(ie3).max)
106+
nexp = floor_log2(type_info(ie3)['max'])
107107
# Values too large for the format
108108
smn, smx = -2**(nexp+1), 2**(nexp+1)
109109
if ie3 is np.float64:
@@ -122,7 +122,7 @@ def test_int_to_float():
122122
assert_equal(int_to_float(i, LD), LD(i))
123123
assert_equal(int_to_float(-i, LD), LD(-i))
124124
# Above max of float64, we're hosed
125-
nexp64 = floor_log2(np.finfo(np.float64).max)
125+
nexp64 = floor_log2(type_info(np.float64)['max'])
126126
smn64, smx64 = -2**(nexp64+1), 2**(nexp64+1)
127127
# The algorithm here implemented goes through float64, so supermax and
128128
# supermin will cause overflow errors
@@ -151,7 +151,7 @@ def test_floor_exact_16():
151151
# A normal integer can generate an inf in float16
152152
if not have_float16:
153153
raise SkipTest('No float16')
154-
assert_equal(floor_exact(2**31, np.float16), np.finfo(np.float16).max)
154+
assert_equal(floor_exact(2**31, np.float16), type_info(np.float16)['max'])
155155

156156

157157
def test_floor_exact_64():
@@ -181,8 +181,9 @@ def test_floor_exact():
181181
int_flex = lambda x, t : as_int(floor_exact(x, t))
182182
for t in to_test:
183183
# A number bigger than the range returns the max
184-
assert_equal(floor_exact(2**5000, t), np.finfo(t).max)
185-
nmant = type_info(t)['nmant']
184+
info = type_info(t)
185+
assert_equal(floor_exact(2**5000, t), info['max'])
186+
nmant = info['nmant']
186187
for i in range(nmant+1):
187188
iv = 2**i
188189
# up to 2**nmant should be exactly representable

nibabel/tests/test_nifti1.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import numpy as np
1616

17+
from ..casting import type_info
1718
from ..tmpdirs import InTemporaryDirectory
1819
from ..spatialimages import HeaderDataError
1920
from .. import nifti1 as nifti1
@@ -71,8 +72,8 @@ def test_big_scaling(self):
7172
sio = BytesIO()
7273
dtt = np.float32
7374
# This will generate a huge scalefactor
74-
finf = np.finfo(dtt)
75-
data = np.array([finf.min, finf.max], dtype=dtt)[:,None, None]
75+
finf = type_info(dtt)
76+
data = np.array([finf['min'], finf['max']], dtype=dtt)[:,None, None]
7677
hdr.data_to_fileobj(data, sio)
7778
data_back = hdr.data_from_fileobj(sio)
7879
assert_true(np.allclose(data, data_back))
@@ -717,8 +718,8 @@ def test_float_int_min_max():
717718
# Parallel test to arraywriters
718719
aff = np.eye(4)
719720
for in_dt in (np.float32, np.float64):
720-
finf = np.finfo(in_dt)
721-
arr = np.array([finf.min, finf.max], dtype=in_dt)
721+
finf = type_info(in_dt)
722+
arr = np.array([finf['min'], finf['max']], dtype=in_dt)
722723
for out_dt in IUINT_TYPES:
723724
img = Nifti1Image(arr, aff)
724725
img_back = round_trip(img)

nibabel/tests/test_scaling.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ..py3k import BytesIO
1717
from ..volumeutils import (calculate_scale, scale_min_max, finite_range,
1818
apply_read_scaling, array_to_file, array_from_file)
19+
from ..casting import type_info
1920

2021
from numpy.testing import (assert_array_almost_equal, assert_array_equal)
2122

@@ -167,17 +168,6 @@ def test_a2f_nan2zero():
167168
assert_array_equal(data_back, [np.array(np.nan).astype(np.int32), 99])
168169

169170

170-
def type_min_max(dtype_type):
171-
# Utility routine to return min, max for dtype dtype
172-
if dtype_type in (np.sctypes['complex'] + np.sctypes['float']):
173-
info = np.finfo(dtype_type)
174-
elif dtype_type in (np.sctypes['int'] + np.sctypes['uint']):
175-
info = np.iinfo(dtype_type)
176-
else:
177-
raise ValueError('Sorry, out of my range')
178-
return info.min, info.max
179-
180-
181171
def test_array_file_scales():
182172
# Test scaling works for max, min when going from larger to smaller type,
183173
# and from float to integer.
@@ -190,7 +180,8 @@ def test_array_file_scales():
190180
(np.float32, np.int16, None)):
191181
out_dtype = np.dtype(out_type)
192182
arr = np.zeros((3,), dtype=in_type)
193-
arr[0], arr[1] = type_min_max(in_type)
183+
info = type_info(in_type)
184+
arr[0], arr[1] = info['min'], info['max']
194185
if not err is None:
195186
assert_raises(err, calculate_scale, arr, out_dtype, True)
196187
continue
@@ -229,7 +220,8 @@ def test_scaling_in_abstract():
229220
def check_int_a2f(in_type, out_type):
230221
# Check that array to / from file returns roughly the same as input
231222
big_floater = np.maximum_sctype(np.float)
232-
this_min, this_max = type_min_max(in_type)
223+
info = type_info(in_type)
224+
this_min, this_max = info['min'], info['max']
233225
if not in_type in np.sctypes['complex']:
234226
data = np.array([this_min, this_max], in_type)
235227
else: # Funny behavior with complex256
@@ -255,6 +247,7 @@ def check_int_a2f(in_type, out_type):
255247
data_back = array_from_file(data.shape, out_type, str_io)
256248
data_back = apply_read_scaling(data_back, scale32, inter32)
257249
# Clip at extremes to remove inf
258-
out_min, out_max = type_min_max(in_type)
250+
info = type_info(in_type)
251+
out_min, out_max = info['min'], info['max']
259252
assert_true(np.allclose(big_floater(data),
260253
big_floater(np.clip(data_back, out_min, out_max))))

nibabel/tests/test_spm99analyze.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
from ..spm99analyze import (Spm99AnalyzeHeader, Spm99AnalyzeImage,
2727
HeaderTypeError)
28+
from ..casting import type_info
2829

2930
from ..testing import (assert_equal, assert_true, assert_false, assert_raises)
3031

@@ -62,7 +63,7 @@ def test_big_scaling(self):
6263
sio = BytesIO()
6364
dtt = np.float32
6465
# This will generate a huge scalefactor
65-
data = np.array([np.finfo(dtt).max], dtype=dtt)[:,None, None]
66+
data = np.array([type_info(dtt)['max']], dtype=dtt)[:,None, None]
6667
hdr.data_to_fileobj(data, sio)
6768
data_back = hdr.data_from_fileobj(sio)
6869
assert_true(np.allclose(data, data_back))

nibabel/tests/test_utils.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,17 @@ def test_a2f_intercept_scale():
129129

130130
def test_a2f_upscale():
131131
# Test working type scales with needed range
132-
info = np.finfo(np.float32)
132+
info = type_info(np.float32)
133133
# Test values discovered from stress testing. The largish value (2**115)
134134
# overflows to inf after the intercept is subtracted, using float32 as the
135135
# working precision. The difference between inf and this value is lost.
136-
arr = np.array([[info.min, 2**115, info.max]], dtype=np.float32)
136+
arr = np.array([[info['min'], 2**115, info['max']]], dtype=np.float32)
137137
slope = np.float32(2**121)
138-
inter = info.min
138+
inter = info['min']
139139
str_io = BytesIO()
140140
# We need to provide mn, mx for function to be able to calculate upcasting
141141
array_to_file(arr, str_io, np.uint8, intercept=inter, divslope=slope,
142-
mn = info.min, mx = info.max)
142+
mn = info['min'], mx = info['max'])
143143
raw = array_from_file(arr.shape, np.uint8, str_io)
144144
back = apply_read_scaling(raw, slope, inter)
145145
top = back - arr
@@ -250,16 +250,16 @@ def test_a2f_zeros():
250250

251251
def test_a2f_big_scalers():
252252
# Check that clip works even for overflowing scalers / data
253-
info = np.finfo(np.float32)
254-
arr = np.array([info.min, np.nan, info.max], dtype=np.float32)
253+
info = type_info(np.float32)
254+
arr = np.array([info['min'], np.nan, info['max']], dtype=np.float32)
255255
str_io = BytesIO()
256256
# Intercept causes overflow - does routine scale correctly?
257257
array_to_file(arr, str_io, np.int8, intercept=np.float32(2**120))
258258
data_back = array_from_file(arr.shape, np.int8, str_io)
259259
assert_array_equal(data_back, [-128, 0, 127])
260260
# Scales also if mx, mn specified?
261261
str_io.seek(0)
262-
array_to_file(arr, str_io, np.int8, mn=info.min, mx=info.max,
262+
array_to_file(arr, str_io, np.int8, mn=info['min'], mx=info['max'],
263263
intercept=np.float32(2**120))
264264
data_back = array_from_file(arr.shape, np.int8, str_io)
265265
assert_array_equal(data_back, [-128, 0, 127])
@@ -270,7 +270,7 @@ def test_a2f_big_scalers():
270270
assert_array_equal(data_back, [-128, 0, 127])
271271
# with mn, mx specified?
272272
str_io.seek(0)
273-
array_to_file(arr, str_io, np.int8, mn=info.min, mx=info.max,
273+
array_to_file(arr, str_io, np.int8, mn=info['min'], mx=info['max'],
274274
divslope=np.float32(0.5))
275275
data_back = array_from_file(arr.shape, np.int8, str_io)
276276
assert_array_equal(data_back, [-128, 0, 127])
@@ -302,7 +302,7 @@ def test_apply_scaling():
302302
ret = apply_read_scaling(np.float32(0), inter=np.float64(2))
303303
assert_equal(ret.dtype, np.float64)
304304
# Check integer inf upcast
305-
big = f32(np.finfo(f32).max)
305+
big = f32(type_info(f32)['max'])
306306
# Normally this would not upcast
307307
assert_equal((i16_arr * big).dtype, np.float32)
308308
# An equivalent case is a little hard to find for the intercept
@@ -410,8 +410,9 @@ def test_best_write_scale_ftype():
410410
best_vals += ((np.float64, np.longdouble),)
411411
for lower_t, higher_t in best_vals:
412412
# Information on this float
413-
t_max = np.finfo(lower_t).max
414-
nmant = type_info(lower_t)['nmant'] # number of significand digits
413+
L_info = type_info(lower_t)
414+
t_max = L_info['max']
415+
nmant = L_info['nmant'] # number of significand digits
415416
big_delta = lower_t(2**(floor_log2(t_max) - nmant)) # delta below max
416417
# Even large values that don't overflow don't change output
417418
arr = np.array([0, t_max], dtype=lower_t)

nibabel/volumeutils.py

Lines changed: 3 additions & 6 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
19+
from .casting import shared_range, type_info
2020

2121
sys_is_le = sys.byteorder == 'little'
2222
native_code = sys_is_le and '<' or '>'
@@ -908,12 +908,9 @@ def scale_min_max(mn, mx, out_type, allow_intercept):
908908
'''
909909
if mn > mx:
910910
raise ValueError('min value > max value')
911-
try:
912-
info = np.iinfo(out_type)
913-
except ValueError:
914-
info = np.finfo(out_type)
911+
info = type_info(out_type)
915912
mn, mx, type_min, type_max = np.array(
916-
[mn, mx, info.min, info.max], np.maximum_sctype(np.float))
913+
[mn, mx, info['min'], info['max']], np.maximum_sctype(np.float))
917914
# with intercept
918915
if allow_intercept:
919916
data_range = mx-mn

0 commit comments

Comments
 (0)