1
""" Test floating point deconstructions and floor methods
3
from platform import processor
7
from ..casting import (floor_exact, ceil_exact, as_int, FloatingError,
8
int_to_float, floor_log2, type_info)
10
from nose import SkipTest
11
from nose.tools import assert_equal, assert_raises
13
IEEE_floats = [np.float32, np.float64]
16
except AttributeError: # float16 not present in np < 1.6
21
IEEE_floats.append(np.float16)
23
LD_INFO = type_info(np.longdouble)
27
# Test routine to get min, max, nmant, nexp
28
for dtt in np.sctypes['int'] + np.sctypes['uint']:
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]:
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),
45
assert_equal(infod['min'].dtype.type, dtt)
46
assert_equal(infod['max'].dtype.type, dtt)
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),
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),
68
elif vals == (52, 15, 12):
69
exp_res = type_info(np.float64)
70
exp_res['width'] = width
71
assert_equal(exp_res, infod)
73
raise ValueError("Unexpected float type to test")
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)
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
97
nmant = type_info(np.longdouble)['nmant']
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)
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):
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)
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
130
# up to integer precision of float64 nmant, we get the same result as for
132
nmant = type_info(np.float64)['nmant']
133
for p in range(nmant+2): # implicit
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)
145
nmant = type_info(np.longdouble)['nmant']
146
except FloatingError: # don't know where to test
148
# Assuming nmant is greater than that for float64, test we recover precision
150
assert_equal(as_int(int_to_float(i, LD)), i)
151
assert_equal(as_int(int_to_float(-i, LD)), -i)
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']:
159
mn, mx = np.array([info.min, info.max], dtype=t)
160
assert_equal((mn, mx), (as_int(mn), as_int(mx)))
163
def test_floor_exact_16():
164
# A normal integer can generate an inf in 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)
171
def test_floor_exact_64():
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)
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))
184
def test_floor_exact():
185
to_test = IEEE_floats + [float]
187
type_info(np.longdouble)['nmant']
188
except FloatingError:
189
# Significand bit count not reliable, don't test long double
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))
198
# A number bigger than the range returns the max
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):
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':
219
# Confirm to ourselves that 2**(nmant+1) can't be exactly represented
221
assert_equal(int_flex(iv+1, t), iv)
222
assert_equal(int_ceex(iv+1, t), iv+2)
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.
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)
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)