Skip to content

Commit 65776dc

Browse files
committed
BF - work round numpy 1.4.1 int casting bug
In numpy 1.4.1 we have ``int(np.uint32(2**32-1) == -1``. This was messing up the calculation of the scalefactor. Thanks to Yarik for finding the test failure in test_scaling_in_abstract.
1 parent 0a1aaab commit 65776dc

File tree

3 files changed

+26
-5
lines changed

3 files changed

+26
-5
lines changed

nibabel/arraywriters.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def __init__(self, array, out_dtype=None)
2323

2424
import numpy as np
2525

26-
from .casting import shared_range, int_to_float
26+
from .casting import shared_range, int_to_float, as_int
2727
from .volumeutils import finite_range, array_to_file
2828

2929

@@ -442,8 +442,9 @@ def _range_scale(self):
442442
else: # max possible (u)int range is 2**64-1 (int64, uint64)
443443
# int_to_float covers this range. On windows longdouble is the same
444444
# as double so mn2mx will be 2**64 - thus overestimating slope
445-
# slightly. Casting to int here is probably not necessary
446-
mn2mx = int_to_float(int(mx) - int(mn), np.longdouble)
445+
# slightly. Casting to int needed to allow mx-mn to be larger than
446+
# the largest (u)int value
447+
mn2mx = int_to_float(as_int(mx) - as_int(mn), np.longdouble)
447448
slope = mn2mx / scaled_mn2mx
448449
self.inter = mn - shared_min * slope
449450
self.slope = slope

nibabel/casting.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,13 +210,16 @@ def as_int(x, check=True):
210210
This is useful because the numpy int(val) mechanism is broken for large
211211
values in np.longdouble.
212212
213+
It is also useful to work around a numpy 1.4.1 bug in conversion of uints to
214+
python ints.
215+
213216
This routine will still raise an OverflowError for values that are outside
214217
the range of float64.
215218
216219
Parameters
217220
----------
218221
x : object
219-
Floating point value
222+
integer, unsigned integer or floating point value
220223
check : {True, False}
221224
If True, raise error for values that are not integers
222225
@@ -238,7 +241,15 @@ def as_int(x, check=True):
238241
>>> as_int(2.1, check=False)
239242
2
240243
"""
241-
x = np.array(x, copy=True)
244+
x = np.array(x)
245+
if x.dtype.kind in 'iu':
246+
# This works around a nasty numpy 1.4.1 bug such that:
247+
# >>> int(np.uint32(2**32-1)
248+
# -1
249+
return int(str(x))
250+
ix = int(x)
251+
if ix == x:
252+
return ix
242253
fx = np.floor(x)
243254
if check and fx != x:
244255
raise FloatingError('Not an integer: %s' % x)

nibabel/tests/test_floating.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ def test_int_to_float():
9797
assert_equal(as_int(int_to_float(-i, LD)), -i)
9898

9999

100+
def test_as_int_np_fix():
101+
# Test as_int works for integers. We need as_int for integers because of a
102+
# numpy 1.4.1 bug such that int(np.uint32(2**32-1) == -1
103+
for t in np.sctypes['int'] + np.sctypes['uint']:
104+
info = np.iinfo(t)
105+
mn, mx = np.array([info.min, info.max], dtype=t)
106+
assert_equal((mn, mx), (as_int(mn), as_int(mx)))
107+
108+
100109
def test_floor_exact_16():
101110
# A normal integer can generate an inf in float16
102111
if not have_float16:

0 commit comments

Comments
 (0)