Skip to content

Commit 0be65d0

Browse files
committed
NF - abs routine to deal with largest -ve int
Default abs leaves largest negative int from int dtypes as still negative. This is particularly confusing for np.allclose.
1 parent 65776dc commit 0be65d0

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

nibabel/casting.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,46 @@ def floor_exact(val, flt_type):
355355
return sign * faval
356356

357357

358+
def int_abs(arr):
359+
""" Absolute values of array taking care of max negative int values
360+
361+
Parameters
362+
----------
363+
arr : array-like
364+
365+
Returns
366+
-------
367+
abs_arr : array
368+
array the same shape as `arr` in which all negative numbers have been
369+
changed to positive numbers with the magnitude.
370+
371+
Examples
372+
--------
373+
This kind of thing is confusing in base numpy:
374+
375+
>>> import numpy as np
376+
>>> np.abs(np.int8(-128))
377+
-128
378+
379+
``int_abs`` fixes that:
380+
381+
>>> int_abs(np.int8(-128))
382+
128
383+
>>> int_abs(np.array([-128, 127], dtype=np.int8))
384+
array([128, 127], dtype=uint8)
385+
>>> int_abs(np.array([-128, 127], dtype=np.float32))
386+
array([ 128., 127.], dtype=float32)
387+
"""
388+
arr = np.array(arr, copy=False)
389+
dt = arr.dtype
390+
if dt.kind == 'u':
391+
return arr
392+
if dt.kind != 'i':
393+
return np.absolute(arr)
394+
out = arr.astype(np.dtype(dt.str.replace('i', 'u')))
395+
return np.choose(arr < 0, (arr, arr * -1), out=out)
396+
397+
358398
def floor_log2(x):
359399
""" floor of log2 of abs(`x`)
360400

nibabel/tests/test_casting.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import numpy as np
55

66
from ..casting import (float_to_int, shared_range, CastingError, int_to_float,
7-
as_int)
7+
as_int, int_abs)
88

99
from numpy.testing import (assert_array_almost_equal, assert_array_equal)
1010

@@ -121,3 +121,18 @@ def test_casting():
121121
assert_array_equal(float_to_int(np.nan, np.int16), [0])
122122
# Test nans give error if not nan2zero
123123
assert_raises(CastingError, float_to_int, np.nan, np.int16, False)
124+
125+
126+
def test_int_abs():
127+
for itype in np.sctypes['int']:
128+
info = np.iinfo(itype)
129+
in_arr = np.array([info.min, info.max], dtype=itype)
130+
idtype = np.dtype(itype)
131+
udtype = np.dtype(idtype.str.replace('i', 'u'))
132+
assert_equal(udtype.kind, 'u')
133+
assert_equal(idtype.itemsize, udtype.itemsize)
134+
mn, mx = in_arr
135+
e_mn = as_int(mx) + 1 # as_int needed for numpy 1.4.1 casting
136+
assert_equal(int_abs(mx), mx)
137+
assert_equal(int_abs(mn), e_mn)
138+
assert_array_equal(int_abs(in_arr), [e_mn, mx])

0 commit comments

Comments
 (0)