~ubuntu-branches/ubuntu/wily/nibabel/wily-proposed

« back to all changes in this revision

Viewing changes to nibabel/tests/test_floating.py

  • Committer: Package Import Robot
  • Author(s): Yaroslav Halchenko
  • Date: 2012-05-06 12:58:22 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20120506125822-3jiwjkmdqcxkrior
Tags: 1.2.0-1
* New upstream release: bugfixes, support for new formats
  (Freesurfer, ECAT, etc), nib-ls tool.
* debian/copyright
  - corrected reference to the format specs
  - points to github's repository as the origin of sources
  - removed lightunit license/copyright -- removed upstream
  - added netcdf module license/copyright terms
* debian/control
  - added python-fuse to Recommends and Build-Depends for dicomfs
  - boosted policy compliance to 3.9.3 (no further changes)
* debian/watch
  - adjusted to download numbered tag

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
""" Test floating point deconstructions and floor methods
 
2
"""
 
3
from platform import processor
 
4
 
 
5
import numpy as np
 
6
 
 
7
from ..casting import (floor_exact, ceil_exact, as_int, FloatingError,
 
8
                       int_to_float, floor_log2, type_info)
 
9
 
 
10
from nose import SkipTest
 
11
from nose.tools import assert_equal, assert_raises
 
12
 
 
13
IEEE_floats = [np.float32, np.float64]
 
14
try:
 
15
    np.float16
 
16
except AttributeError: # float16 not present in np < 1.6
 
17
    have_float16 = False
 
18
else:
 
19
    have_float16 = True
 
20
if have_float16:
 
21
    IEEE_floats.append(np.float16)
 
22
 
 
23
LD_INFO = type_info(np.longdouble)
 
24
 
 
25
 
 
26
def test_type_info():
 
27
    # Test routine to get min, max, nmant, nexp
 
28
    for dtt in np.sctypes['int'] + np.sctypes['uint']:
 
29
        info = np.iinfo(dtt)
 
30
        infod = type_info(dtt)
 
31
        assert_equal(dict(min=info.min, max=info.max,
 
32
                          nexp=None, nmant=None,
 
33
                          minexp=None, maxexp=None,
 
34
                          width=np.dtype(dtt).itemsize), infod)
 
35
        assert_equal(infod['min'].dtype.type, dtt)
 
36
        assert_equal(infod['max'].dtype.type, dtt)
 
37
    for dtt in IEEE_floats + [np.complex64, np.complex64]:
 
38
        info = np.finfo(dtt)
 
39
        infod = type_info(dtt)
 
40
        assert_equal(dict(min=info.min, max=info.max,
 
41
                          nexp=info.nexp, nmant=info.nmant,
 
42
                          minexp=info.minexp, maxexp=info.maxexp,
 
43
                          width=np.dtype(dtt).itemsize),
 
44
                     infod)
 
45
        assert_equal(infod['min'].dtype.type, dtt)
 
46
        assert_equal(infod['max'].dtype.type, dtt)
 
47
    # What is longdouble?
 
48
    info = np.finfo(np.longdouble)
 
49
    dbl_info = np.finfo(np.float64)
 
50
    infod = type_info(np.longdouble)
 
51
    width = np.dtype(np.longdouble).itemsize
 
52
    vals = (info.nmant, info.nexp, width)
 
53
    # Information for PPC head / tail doubles from:
 
54
    # https://developer.apple.com/library/mac/#documentation/Darwin/Reference/Manpages/man3/float.3.html
 
55
    if vals in ((52, 11, 8), # longdouble is same as double
 
56
                (63, 15, 12), (63, 15, 16), # intel 80 bit
 
57
                (112, 15, 16), # real float128
 
58
                (106, 11, 16)): # PPC head, tail doubles, expected values
 
59
        assert_equal(dict(min=info.min, max=info.max,
 
60
                          minexp=info.minexp, maxexp=info.maxexp,
 
61
                          nexp=info.nexp, nmant=info.nmant, width=width),
 
62
                     infod)
 
63
    elif vals == (1, 1, 16): # bust info for PPC head / tail longdoubles
 
64
        assert_equal(dict(min=dbl_info.min, max=dbl_info.max,
 
65
                          minexp=-1022, maxexp=1024,
 
66
                          nexp=11, nmant=106, width=16),
 
67
                     infod)
 
68
    elif vals == (52, 15, 12):
 
69
        exp_res = type_info(np.float64)
 
70
        exp_res['width'] = width
 
71
        assert_equal(exp_res, infod)
 
72
    else:
 
73
        raise ValueError("Unexpected float type to test")
 
74
 
 
75
 
 
76
def test_nmant():
 
77
    for t in IEEE_floats:
 
78
        assert_equal(type_info(t)['nmant'], np.finfo(t).nmant)
 
79
    if (LD_INFO['nmant'], LD_INFO['nexp']) == (63, 15):
 
80
        assert_equal(type_info(np.longdouble)['nmant'], 63)
 
81
 
 
82
 
 
83
def test_as_int():
 
84
    # Integer representation of number
 
85
    assert_equal(as_int(2.0), 2)
 
86
    assert_equal(as_int(-2.0), -2)
 
87
    assert_raises(FloatingError, as_int, 2.1)
 
88
    assert_raises(FloatingError, as_int, -2.1)
 
89
    assert_equal(as_int(2.1, False), 2)
 
90
    assert_equal(as_int(-2.1, False), -2)
 
91
    v = np.longdouble(2**64)
 
92
    assert_equal(as_int(v), 2**64)
 
93
    # Have all long doubles got 63+1 binary bits of precision?  Windows 32-bit
 
94
    # longdouble appears to have 52 bit precision, but we avoid that by checking
 
95
    # for known precisions that are less than that required
 
96
    try:
 
97
        nmant = type_info(np.longdouble)['nmant']
 
98
    except FloatingError:
 
99
        nmant = 63 # Unknown precision, let's hope it's at least 63
 
100
    v = np.longdouble(2) ** (nmant + 1) - 1
 
101
    assert_equal(as_int(v), 2**(nmant + 1) -1)
 
102
    # Check for predictable overflow
 
103
    nexp64 = floor_log2(type_info(np.float64)['max'])
 
104
    val = np.longdouble(2**nexp64) * 2 # outside float64 range
 
105
    assert_raises(OverflowError, as_int, val)
 
106
    assert_raises(OverflowError, as_int, -val)
 
107
 
 
108
 
 
109
def test_int_to_float():
 
110
    # Concert python integer to floating point
 
111
    # Standard float types just return cast value
 
112
    for ie3 in IEEE_floats:
 
113
        nmant = type_info(ie3)['nmant']
 
114
        for p in range(nmant + 3):
 
115
            i = 2**p+1
 
116
            assert_equal(int_to_float(i, ie3), ie3(i))
 
117
            assert_equal(int_to_float(-i, ie3), ie3(-i))
 
118
        # IEEEs in this case are binary formats only
 
119
        nexp = floor_log2(type_info(ie3)['max'])
 
120
        # Values too large for the format
 
121
        smn, smx = -2**(nexp+1), 2**(nexp+1)
 
122
        if ie3 is np.float64:
 
123
            assert_raises(OverflowError, int_to_float, smn, ie3)
 
124
            assert_raises(OverflowError, int_to_float, smx, ie3)
 
125
        else:
 
126
            assert_equal(int_to_float(smn, ie3), ie3(smn))
 
127
            assert_equal(int_to_float(smx, ie3), ie3(smx))
 
128
    # Longdoubles do better than int, we hope
 
129
    LD = np.longdouble
 
130
    # up to integer precision of float64 nmant, we get the same result as for
 
131
    # casting directly
 
132
    nmant = type_info(np.float64)['nmant']
 
133
    for p in range(nmant+2): # implicit
 
134
        i = 2**p-1
 
135
        assert_equal(int_to_float(i, LD), LD(i))
 
136
        assert_equal(int_to_float(-i, LD), LD(-i))
 
137
    # Above max of float64, we're hosed
 
138
    nexp64 = floor_log2(type_info(np.float64)['max'])
 
139
    smn64, smx64 = -2**(nexp64+1), 2**(nexp64+1)
 
140
    # The algorithm here implemented goes through float64, so supermax and
 
141
    # supermin will cause overflow errors
 
142
    assert_raises(OverflowError, int_to_float, smn64, LD)
 
143
    assert_raises(OverflowError, int_to_float, smx64, LD)
 
144
    try:
 
145
        nmant = type_info(np.longdouble)['nmant']
 
146
    except FloatingError: # don't know where to test
 
147
        return
 
148
    # Assuming nmant is greater than that for float64, test we recover precision
 
149
    i = 2**(nmant+1)-1
 
150
    assert_equal(as_int(int_to_float(i, LD)), i)
 
151
    assert_equal(as_int(int_to_float(-i, LD)), -i)
 
152
 
 
153
 
 
154
def test_as_int_np_fix():
 
155
    # Test as_int works for integers.  We need as_int for integers because of a
 
156
    # numpy 1.4.1 bug such that int(np.uint32(2**32-1) == -1
 
157
    for t in np.sctypes['int'] + np.sctypes['uint']:
 
158
        info = np.iinfo(t)
 
159
        mn, mx = np.array([info.min, info.max], dtype=t)
 
160
        assert_equal((mn, mx), (as_int(mn), as_int(mx)))
 
161
 
 
162
 
 
163
def test_floor_exact_16():
 
164
    # A normal integer can generate an inf in float16
 
165
    if not have_float16:
 
166
        raise SkipTest('No float16')
 
167
    assert_equal(floor_exact(2**31, np.float16), np.inf)
 
168
    assert_equal(floor_exact(-2**31, np.float16), -np.inf)
 
169
 
 
170
 
 
171
def test_floor_exact_64():
 
172
    # float64
 
173
    for e in range(53, 63):
 
174
        start = np.float64(2**e)
 
175
        across = start + np.arange(2048, dtype=np.float64)
 
176
        gaps = set(np.diff(across)).difference([0])
 
177
        assert_equal(len(gaps), 1)
 
178
        gap = gaps.pop()
 
179
        assert_equal(gap, int(gap))
 
180
        test_val = 2**(e+1)-1
 
181
        assert_equal(floor_exact(test_val, np.float64), 2**(e+1)-int(gap))
 
182
 
 
183
 
 
184
def test_floor_exact():
 
185
    to_test = IEEE_floats + [float]
 
186
    try:
 
187
        type_info(np.longdouble)['nmant']
 
188
    except FloatingError:
 
189
        # Significand bit count not reliable, don't test long double
 
190
        pass
 
191
    else:
 
192
        to_test.append(np.longdouble)
 
193
    # When numbers go above int64 - I believe, numpy comparisons break down,
 
194
    # so we have to cast to int before comparison
 
195
    int_flex = lambda x, t : as_int(floor_exact(x, t))
 
196
    int_ceex = lambda x, t : as_int(ceil_exact(x, t))
 
197
    for t in to_test:
 
198
        # A number bigger than the range returns the max
 
199
        info = type_info(t)
 
200
        assert_equal(floor_exact(2**5000, t), np.inf)
 
201
        assert_equal(ceil_exact(2**5000, t), np.inf)
 
202
        # A number more negative returns -inf
 
203
        assert_equal(floor_exact(-2**5000, t), -np.inf)
 
204
        assert_equal(ceil_exact(-2**5000, t), -np.inf)
 
205
        # Check around end of integer precision
 
206
        nmant =  info['nmant']
 
207
        for i in range(nmant+1):
 
208
            iv = 2**i
 
209
            # up to 2**nmant should be exactly representable
 
210
            for func in (int_flex, int_ceex):
 
211
                assert_equal(func(iv, t), iv)
 
212
                assert_equal(func(-iv, t), -iv)
 
213
                assert_equal(func(iv-1, t), iv-1)
 
214
                assert_equal(func(-iv+1, t), -iv+1)
 
215
        # The nmant value for longdouble on PPC appears to be conservative, so
 
216
        # that the tests for behavior above the nmant range fail
 
217
        if t is np.longdouble and processor() == 'powerpc':
 
218
            continue
 
219
        # Confirm to ourselves that 2**(nmant+1) can't be exactly represented
 
220
        iv = 2**(nmant+1)
 
221
        assert_equal(int_flex(iv+1, t), iv)
 
222
        assert_equal(int_ceex(iv+1, t), iv+2)
 
223
        # negatives
 
224
        assert_equal(int_flex(-iv-1, t), -iv-2)
 
225
        assert_equal(int_ceex(-iv-1, t), -iv)
 
226
        # The gap in representable numbers is 2 above 2**(nmant+1), 4 above
 
227
        # 2**(nmant+2), and so on.
 
228
        for i in range(5):
 
229
            iv = 2**(nmant+1+i)
 
230
            gap = 2**(i+1)
 
231
            assert_equal(as_int(t(iv) + t(gap)), iv+gap)
 
232
            for j in range(1, gap):
 
233
                assert_equal(int_flex(iv+j, t), iv)
 
234
                assert_equal(int_flex(iv+gap+j, t), iv+gap)
 
235
                assert_equal(int_ceex(iv+j, t), iv+gap)
 
236
                assert_equal(int_ceex(iv+gap+j, t), iv+2*gap)
 
237
            # negatives
 
238
            for j in range(1, gap):
 
239
                assert_equal(int_flex(-iv-j, t), -iv-gap)
 
240
                assert_equal(int_flex(-iv-gap-j, t), -iv-2*gap)
 
241
                assert_equal(int_ceex(-iv-j, t), -iv)
 
242
                assert_equal(int_ceex(-iv-gap-j, t), -iv-gap)