Skip to content

Commit f815aba

Browse files
authored
Merge pull request #1272 from effigies/rf/remove-longdouble-hacks
RF: Deprecate longdouble<->int hacks
2 parents dabfc46 + bbfd009 commit f815aba

File tree

7 files changed

+76
-177
lines changed

7 files changed

+76
-177
lines changed

nibabel/arraywriters.py

+16-27
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,7 @@ def __init__(self, array, out_dtype=None)
3030
"""
3131
import numpy as np
3232

33-
from .casting import (
34-
as_int,
35-
best_float,
36-
floor_exact,
37-
int_abs,
38-
int_to_float,
39-
shared_range,
40-
type_info,
41-
)
33+
from .casting import best_float, floor_exact, int_abs, shared_range, type_info
4234
from .volumeutils import array_to_file, finite_range
4335

4436

@@ -152,9 +144,8 @@ def scaling_needed(self):
152144
# No scaling needed if data already fits in output type
153145
# But note - we need to convert to ints, to avoid conversion to float
154146
# during comparisons, and therefore int -> float conversions which are
155-
# not exact. Only a problem for uint64 though. We need as_int here to
156-
# work around a numpy 1.4.1 bug in uint conversion
157-
if as_int(mn) >= as_int(info.min) and as_int(mx) <= as_int(info.max):
147+
# not exact. Only a problem for uint64 though.
148+
if int(mn) >= int(info.min) and int(mx) <= int(info.max):
158149
return False
159150
return True
160151

@@ -392,7 +383,7 @@ def _do_scaling(self):
392383
out_max, out_min = info.max, info.min
393384
# If left as int64, uint64, comparisons will default to floats, and
394385
# these are inexact for > 2**53 - so convert to int
395-
if as_int(mx) <= as_int(out_max) and as_int(mn) >= as_int(out_min):
386+
if int(mx) <= int(out_max) and int(mn) >= int(out_min):
396387
# already in range
397388
return
398389
# (u)int to (u)int scaling
@@ -410,7 +401,7 @@ def _iu2iu(self):
410401
# that deals with max neg ints. abs problem only arises when all
411402
# the data is set to max neg integer value
412403
o_min, o_max = shared_range(self.scaler_dtype, out_dt)
413-
if mx <= 0 and int_abs(mn) <= as_int(o_max): # sign flip enough?
404+
if mx <= 0 and int_abs(mn) <= int(o_max): # sign flip enough?
414405
# -1.0 * arr will be in scaler_dtype precision
415406
self.slope = -1.0
416407
return
@@ -427,7 +418,7 @@ def _range_scale(self, in_min, in_max):
427418
# not lose precision because min/max are of fp type.
428419
out_min, out_max = np.array((out_min, out_max), dtype=big_float)
429420
else: # (u)int
430-
out_min, out_max = (int_to_float(v, big_float) for v in (out_min, out_max))
421+
out_min, out_max = (big_float(v) for v in (out_min, out_max))
431422
if self._out_dtype.kind == 'u':
432423
if in_min < 0 and in_max > 0:
433424
raise WriterError(
@@ -546,14 +537,13 @@ def to_fileobj(self, fileobj, order='F'):
546537

547538
def _iu2iu(self):
548539
# (u)int to (u)int
549-
mn, mx = (as_int(v) for v in self.finite_range())
540+
mn, mx = (int(v) for v in self.finite_range())
550541
# range may be greater than the largest integer for this type.
551-
# as_int needed to work round numpy 1.4.1 int casting bug
552542
out_dtype = self._out_dtype
553543
# Options in this method are scaling using intercept only. These will
554544
# have to pass through ``self.scaler_dtype`` (because the intercept is
555545
# in this type).
556-
o_min, o_max = (as_int(v) for v in shared_range(self.scaler_dtype, out_dtype))
546+
o_min, o_max = (int(v) for v in shared_range(self.scaler_dtype, out_dtype))
557547
type_range = o_max - o_min
558548
mn2mx = mx - mn
559549
if mn2mx <= type_range: # might offset be enough?
@@ -565,12 +555,12 @@ def _iu2iu(self):
565555
else: # int output - take midpoint to 0
566556
# ceil below increases inter, pushing scale up to 0.5 towards
567557
# -inf, because ints have abs min == abs max + 1
568-
midpoint = mn + as_int(np.ceil(mn2mx / 2.0))
558+
midpoint = mn + int(np.ceil(mn2mx / 2.0))
569559
# Floor exact decreases inter, so pulling scaled values more
570560
# positive. This may make mx - inter > t_max
571561
inter = floor_exact(midpoint, self.scaler_dtype)
572562
# Need to check still in range after floor_exact-ing
573-
int_inter = as_int(inter)
563+
int_inter = int(inter)
574564
assert mn - int_inter >= o_min
575565
if mx - int_inter <= o_max:
576566
self.inter = inter
@@ -594,14 +584,13 @@ def _range_scale(self, in_min, in_max):
594584
in_min, in_max = np.array([in_min, in_max], dtype=big_float)
595585
in_range = np.diff([in_min, in_max])
596586
else: # max possible (u)int range is 2**64-1 (int64, uint64)
597-
# int_to_float covers this range. On windows longdouble is the
598-
# same as double so in_range will be 2**64 - thus overestimating
599-
# slope slightly. Casting to int needed to allow in_max-in_min to
600-
# be larger than the largest (u)int value
601-
in_min, in_max = as_int(in_min), as_int(in_max)
602-
in_range = int_to_float(in_max - in_min, big_float)
587+
# On windows longdouble is the same as double so in_range will be 2**64 -
588+
# thus overestimating slope slightly. Casting to int needed to allow
589+
# in_max-in_min to be larger than the largest (u)int value
590+
in_min, in_max = int(in_min), int(in_max)
591+
in_range = big_float(in_max - in_min)
603592
# Cast to float for later processing.
604-
in_min, in_max = (int_to_float(v, big_float) for v in (in_min, in_max))
593+
in_min, in_max = (big_float(v) for v in (in_min, in_max))
605594
if out_dtype.kind == 'f':
606595
# Type range, these are also floats
607596
info = type_info(out_dtype)

nibabel/casting.py

+15-37
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
import numpy as np
1212

13+
from .deprecated import deprecate_with_version
14+
1315

1416
class CastingError(Exception):
1517
pass
@@ -402,6 +404,7 @@ def _check_maxexp(np_type, maxexp):
402404
return np.isfinite(two ** (maxexp - 1)) and not np.isfinite(two**maxexp)
403405

404406

407+
@deprecate_with_version('as_int() is deprecated. Use int() instead.', '5.2.0', '7.0.0')
405408
def as_int(x, check=True):
406409
"""Return python integer representation of number
407410
@@ -411,9 +414,6 @@ def as_int(x, check=True):
411414
It is also useful to work around a numpy 1.4.1 bug in conversion of uints
412415
to python ints.
413416
414-
This routine will still raise an OverflowError for values that are outside
415-
the range of float64.
416-
417417
Parameters
418418
----------
419419
x : object
@@ -439,30 +439,13 @@ def as_int(x, check=True):
439439
>>> as_int(2.1, check=False)
440440
2
441441
"""
442-
x = np.array(x)
443-
if x.dtype.kind in 'iu':
444-
# This works around a nasty numpy 1.4.1 bug such that:
445-
# >>> int(np.uint32(2**32-1)
446-
# -1
447-
return int(str(x))
448442
ix = int(x)
449-
if ix == x:
450-
return ix
451-
fx = np.floor(x)
452-
if check and fx != x:
443+
if check and ix != x:
453444
raise FloatingError(f'Not an integer: {x}')
454-
if not fx.dtype.type == np.longdouble:
455-
return int(x)
456-
# Subtract float64 chunks until we have all of the number. If the int is
457-
# too large, it will overflow
458-
ret = 0
459-
while fx != 0:
460-
f64 = np.float64(fx)
461-
fx -= f64
462-
ret += int(f64)
463-
return ret
445+
return ix
464446

465447

448+
@deprecate_with_version('int_to_float(..., dt) is deprecated. Use dt() instead.', '5.2.0', '7.0.0')
466449
def int_to_float(val, flt_type):
467450
"""Convert integer `val` to floating point type `flt_type`
468451
@@ -484,18 +467,13 @@ def int_to_float(val, flt_type):
484467
-------
485468
f : numpy scalar
486469
of type `flt_type`
470+
471+
Examples
472+
--------
473+
>>> int_to_float(1, np.float32)
474+
1.0
487475
"""
488-
if flt_type is not np.longdouble:
489-
return flt_type(val)
490-
# The following works around a nasty numpy 1.4.1 bug such that:
491-
# >>> int(np.uint32(2**32-1)
492-
val = int(val)
493-
faval = np.longdouble(0)
494-
while val != 0:
495-
f64 = np.float64(val)
496-
faval += f64
497-
val -= int(f64)
498-
return faval
476+
return flt_type(val)
499477

500478

501479
def floor_exact(val, flt_type):
@@ -542,14 +520,14 @@ def floor_exact(val, flt_type):
542520
val = int(val)
543521
flt_type = np.dtype(flt_type).type
544522
sign = 1 if val > 0 else -1
545-
try: # int_to_float deals with longdouble safely
546-
fval = int_to_float(val, flt_type)
523+
try:
524+
fval = flt_type(val)
547525
except OverflowError:
548526
return sign * np.inf
549527
if not np.isfinite(fval):
550528
return fval
551529
info = type_info(flt_type)
552-
diff = val - as_int(fval)
530+
diff = val - int(fval)
553531
if diff >= 0: # floating point value <= val
554532
return fval
555533
# Float casting made the value go up

nibabel/conftest.py

+17
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import sys
2+
13
import numpy as np
24
import pytest
35

@@ -12,3 +14,18 @@ def legacy_printoptions():
1214

1315
if Version(np.__version__) >= Version('1.22'):
1416
np.set_printoptions(legacy='1.21')
17+
18+
19+
@pytest.fixture
20+
def max_digits():
21+
# Set maximum number of digits for int/str conversion for
22+
# duration of a test
23+
try:
24+
orig_max_str_digits = sys.get_int_max_str_digits()
25+
yield sys.set_int_max_str_digits
26+
sys.set_int_max_str_digits(orig_max_str_digits)
27+
except AttributeError: # pragma: no cover
28+
# Nothing to do for versions of Python that lack these methods
29+
# They were added as DoS protection in Python 3.11 and backported to
30+
# some other versions.
31+
yield lambda x: None

nibabel/tests/test_analyze.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from .. import imageglobals
2727
from ..analyze import AnalyzeHeader, AnalyzeImage
2828
from ..arraywriters import WriterError
29-
from ..casting import as_int, sctypes_aliases
29+
from ..casting import sctypes_aliases
3030
from ..nifti1 import Nifti1Header
3131
from ..optpkg import optional_package
3232
from ..spatialimages import HeaderDataError, HeaderTypeError, supported_np_types
@@ -308,8 +308,7 @@ def test_shapes(self):
308308
assert hdr.get_data_shape() == shape
309309
# Check max works, but max+1 raises error
310310
dim_dtype = hdr.structarr['dim'].dtype
311-
# as_int for safety to deal with numpy 1.4.1 int conversion errors
312-
mx = as_int(np.iinfo(dim_dtype).max)
311+
mx = int(np.iinfo(dim_dtype).max)
313312
shape = (mx,)
314313
hdr.set_data_shape(shape)
315314
assert hdr.get_data_shape() == shape

nibabel/tests/test_casting.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@
1010
from ..casting import (
1111
CastingError,
1212
able_int_type,
13-
as_int,
1413
best_float,
1514
float_to_int,
1615
floor_log2,
1716
int_abs,
18-
int_to_float,
1917
longdouble_precision_improved,
2018
sctypes,
2119
shared_range,
@@ -42,7 +40,7 @@ def test_shared_range():
4240
if casted_mx != imax:
4341
# The shared_range have told us that they believe the imax does
4442
# not have an exact representation.
45-
fimax = int_to_float(imax, ft)
43+
fimax = ft(imax)
4644
if np.isfinite(fimax):
4745
assert int(fimax) != imax
4846
# Therefore the imax, cast back to float, and to integer, will
@@ -68,7 +66,7 @@ def test_shared_range():
6866
if casted_mn != imin:
6967
# The shared_range have told us that they believe the imin does
7068
# not have an exact representation.
71-
fimin = int_to_float(imin, ft)
69+
fimin = ft(imin)
7270
if np.isfinite(fimin):
7371
assert int(fimin) != imin
7472
# Therefore the imin, cast back to float, and to integer, will
@@ -101,11 +99,6 @@ def test_casting():
10199
mn, mx = shared_range(ft, it)
102100
with np.errstate(invalid='ignore'):
103101
iarr = float_to_int(farr, it)
104-
# Dammit - for long doubles we need to jump through some hoops not
105-
# to round to numbers outside the range
106-
if ft is np.longdouble:
107-
mn = as_int(mn)
108-
mx = as_int(mx)
109102
exp_arr = np.array([mn, mx, mn, mx, 0, 0, 11], dtype=it)
110103
assert_array_equal(iarr, exp_arr)
111104
# Now test infmax version
@@ -149,7 +142,7 @@ def test_int_abs():
149142
assert udtype.kind == 'u'
150143
assert idtype.itemsize == udtype.itemsize
151144
mn, mx = in_arr
152-
e_mn = as_int(mx) + 1 # as_int needed for numpy 1.4.1 casting
145+
e_mn = int(mx) + 1
153146
assert int_abs(mx) == mx
154147
assert int_abs(mn) == e_mn
155148
assert_array_equal(int_abs(in_arr), [e_mn, mx])

0 commit comments

Comments
 (0)