Author: Brian Kearns <[email protected]>
Branch:
Changeset: r61301:0a025c478fca
Date: 2013-02-15 21:53 -0500
http://bitbucket.org/pypy/pypy/changeset/0a025c478fca/
Log: clean up rstruct.ieee 80-bit float packing and improve tests
diff --git a/pypy/module/micronumpy/test/test_numarray.py
b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -1745,13 +1745,13 @@
assert a[1] == 0xff
assert len(a.data) == 16
-
def test_explicit_dtype_conversion(self):
from _numpypy import array
a = array([1.0, 2.0])
b = array(a, dtype='d')
assert a.dtype is b.dtype
+
class AppTestMultiDim(BaseNumpyAppTest):
def test_init(self):
import _numpypy
diff --git a/pypy/module/micronumpy/types.py b/pypy/module/micronumpy/types.py
--- a/pypy/module/micronumpy/types.py
+++ b/pypy/module/micronumpy/types.py
@@ -1530,8 +1530,8 @@
def byteswap(self, w_v):
value = self.unbox(w_v)
- result = StringBuilder(12)
- pack_float80(result, value, not native_is_bigendian)
+ result = StringBuilder(10)
+ pack_float80(result, value, 10, not native_is_bigendian)
return self.box(unpack_float80(result.build(),
native_is_bigendian))
NonNativeFloat96 = Float96
@@ -1560,8 +1560,8 @@
def byteswap(self, w_v):
value = self.unbox(w_v)
- result = StringBuilder(16)
- pack_float80(result, value, not native_is_bigendian)
+ result = StringBuilder(10)
+ pack_float80(result, value, 10, not native_is_bigendian)
return self.box(unpack_float80(result.build(),
native_is_bigendian))
NonNativeFloat128 = Float128
diff --git a/rpython/rlib/rstruct/ieee.py b/rpython/rlib/rstruct/ieee.py
--- a/rpython/rlib/rstruct/ieee.py
+++ b/rpython/rlib/rstruct/ieee.py
@@ -25,26 +25,23 @@
return int_part
def float_unpack(Q, size):
- """Convert a 16-bit, 32-bit 64-bit integer created
+ """Convert a 16-bit, 32-bit, or 64-bit integer created
by float_pack into a Python float."""
if size == 8:
MIN_EXP = -1021 # = sys.float_info.min_exp
MAX_EXP = 1024 # = sys.float_info.max_exp
MANT_DIG = 53 # = sys.float_info.mant_dig
BITS = 64
- one = r_ulonglong(1)
elif size == 4:
MIN_EXP = -125 # C's FLT_MIN_EXP
MAX_EXP = 128 # FLT_MAX_EXP
MANT_DIG = 24 # FLT_MANT_DIG
BITS = 32
- one = r_ulonglong(1)
elif size == 2:
MIN_EXP = -13
MAX_EXP = 16
MANT_DIG = 11
BITS = 16
- one = r_ulonglong(1)
else:
raise ValueError("invalid size value")
@@ -56,6 +53,7 @@
raise ValueError("input '%r' out of range '%r'" % (Q, Q>>BITS))
# extract pieces with assumed 1.mant values
+ one = r_ulonglong(1)
sign = rarithmetic.intmask(Q >> BITS - 1)
exp = rarithmetic.intmask((Q & ((one << BITS - 1) - (one << MANT_DIG -
1))) >> MANT_DIG - 1)
mant = Q & ((one << MANT_DIG - 1) - 1)
@@ -72,27 +70,32 @@
result = math.ldexp(mant, exp + MIN_EXP - MANT_DIG - 1)
return -result if sign else result
-def float_unpack80(QQ):
+def float_unpack80(QQ, size):
'''Unpack a (mant, exp) tuple of r_ulonglong in 80-bit extended format
into a long double float
'''
- MIN_EXP = -16381
- MAX_EXP = 16384
- MANT_DIG = 64
- TOPBITS = 80 - 64
- one = r_ulonglong(1)
+ if size == 10 or size == 12 or size == 16:
+ MIN_EXP = -16381
+ MAX_EXP = 16384
+ MANT_DIG = 64
+ TOP_BITS = 80 - 64
+ else:
+ raise ValueError("invalid size value")
+
if len(QQ) != 2:
raise ValueError("QQ must be two 64 bit uints")
+
if not objectmodel.we_are_translated():
# This tests generates wrong code when translated:
# with gcc, shifting a 64bit int by 64 bits does
# not change the value.
- if QQ[1] >> TOPBITS:
- raise ValueError("input '%r' out of range '%r'" % (QQ,
QQ[1]>>TOPBITS))
+ if QQ[1] >> TOP_BITS:
+ raise ValueError("input '%r' out of range '%r'" % (QQ,
QQ[1]>>TOP_BITS))
# extract pieces with explicit one in MANT_DIG
- sign = rarithmetic.intmask(QQ[1] >> TOPBITS - 1)
- exp = rarithmetic.intmask((QQ[1] & ((one << TOPBITS - 1) - 1)))
+ one = r_ulonglong(1)
+ sign = rarithmetic.intmask(QQ[1] >> TOP_BITS - 1)
+ exp = rarithmetic.intmask((QQ[1] & ((one << TOP_BITS - 1) - 1)))
mant = QQ[0]
if exp == MAX_EXP - MIN_EXP + 2:
@@ -171,14 +174,18 @@
sign = r_ulonglong(sign)
return ((sign << BITS - 1) | (exp << MANT_DIG - 1)) | mant
-def float_pack80(x):
+def float_pack80(x, size):
"""Convert a Python float or longfloat x into two 64-bit unsigned integers
with 80 bit extended representation."""
- MIN_EXP = -16381
- MAX_EXP = 16384
- MANT_DIG = 64
- BITS = 80
x = float(x) # longfloat not really supported
+ if size == 10 or size == 12 or size == 16:
+ MIN_EXP = -16381
+ MAX_EXP = 16384
+ MANT_DIG = 64
+ BITS = 80
+ else:
+ raise ValueError("invalid size value")
+
sign = rfloat.copysign(1.0, x) < 0.0
if not rfloat.isfinite(x):
if rfloat.isinf(x):
@@ -235,32 +242,32 @@
result.append("".join(l))
@jit.unroll_safe
-def pack_float80(result, x, be):
+def pack_float80(result, x, size, be):
l = []
- unsigned = float_pack80(x)
+ unsigned = float_pack80(x, size)
for i in range(8):
l.append(chr((unsigned[0] >> (i * 8)) & 0xFF))
for i in range(2):
l.append(chr((unsigned[1] >> (i * 8)) & 0xFF))
+ for i in range(size - 10):
+ l.append('\x00')
if be:
l.reverse()
result.append("".join(l))
def unpack_float(s, be):
unsigned = r_ulonglong(0)
- for i in range(len(s)):
- c = ord(s[len(s) - 1 - i if be else i])
+ for i in range(min(len(s), 8)):
+ c = ord(s[-i - 1 if be else i])
unsigned |= r_ulonglong(c) << (i * 8)
return float_unpack(unsigned, len(s))
def unpack_float80(s, be):
- if len(s) != 10:
- raise ValueError
QQ = [r_ulonglong(0), r_ulonglong(0)]
for i in range(8):
- c = ord(s[9 - i if be else i])
+ c = ord(s[-i - 1 if be else i])
QQ[0] |= r_ulonglong(c) << (i * 8)
for i in range(8, 10):
- c = ord(s[9 - i if be else i])
+ c = ord(s[-i - 1 if be else i])
QQ[1] |= r_ulonglong(c) << ((i - 8) * 8)
- return float_unpack80(QQ)
+ return float_unpack80(QQ, len(s))
diff --git a/rpython/rlib/rstruct/test/test_ieee.py
b/rpython/rlib/rstruct/test/test_ieee.py
--- a/rpython/rlib/rstruct/test/test_ieee.py
+++ b/rpython/rlib/rstruct/test/test_ieee.py
@@ -8,6 +8,55 @@
from rpython.translator.c.test.test_genc import compile
+class TestFloatSpecific:
+ def test_halffloat_exact(self):
+ #testcases generated from numpy.float16(x).view('uint16')
+ cases = [[0, 0], [10, 18688], [-10, 51456], [10e3, 28898],
+ [float('inf'), 31744], [-float('inf'), 64512]]
+ for c, h in cases:
+ hbit = ieee.float_pack(c, 2)
+ assert hbit == h
+ assert c == ieee.float_unpack(h, 2)
+
+ def test_halffloat_inexact(self):
+ #testcases generated from numpy.float16(x).view('uint16')
+ cases = [[10.001, 18688, 10.], [-10.001, 51456, -10],
+ [0.027588, 10000, 0.027587890625],
+ [22001, 30047, 22000]]
+ for c, h, f in cases:
+ hbit = ieee.float_pack(c, 2)
+ assert hbit == h
+ assert f == ieee.float_unpack(h, 2)
+
+ def test_halffloat_overunderflow(self):
+ import math
+ cases = [[670000, float('inf')], [-67000, -float('inf')],
+ [1e-08, 0], [-1e-8, -0.]]
+ for f1, f2 in cases:
+ try:
+ f_out = ieee.float_unpack(ieee.float_pack(f1, 2), 2)
+ except OverflowError:
+ f_out = math.copysign(float('inf'), f1)
+ assert f_out == f2
+ assert math.copysign(1., f_out) == math.copysign(1., f2)
+
+ def test_float80_exact(self):
+ s = []
+ ieee.pack_float80(s, -1., 16, False)
+ assert repr(s[-1]) ==
repr('\x00\x00\x00\x00\x00\x00\x00\x80\xff\xbf\x00\x00\x00\x00\x00\x00')
+ ieee.pack_float80(s, -1., 16, True)
+ assert repr(s[-1]) ==
repr('\x00\x00\x00\x00\x00\x00\xbf\xff\x80\x00\x00\x00\x00\x00\x00\x00')
+ ieee.pack_float80(s, -123.456, 16, False)
+ assert repr(s[-1]) ==
repr('\x00\xb8\xf3\xfd\xd4x\xe9\xf6\x05\xc0\x00\x00\x00\x00\x00\x00')
+ ieee.pack_float80(s, -123.456, 16, True)
+ assert repr(s[-1]) ==
repr('\x00\x00\x00\x00\x00\x00\xc0\x05\xf6\xe9x\xd4\xfd\xf3\xb8\x00')
+
+ x =
ieee.unpack_float80('\x00\x00\x00\x00\x00\x00\x00\x80\xff?\xc8\x01\x00\x00\x00\x00',
False)
+ assert x == 1.0
+ x =
ieee.unpack_float80('\x00\x00\x7f\x83\xe1\x91?\xff\x80\x00\x00\x00\x00\x00\x00\x00',
True)
+ assert x == 1.0
+
+
class TestFloatPacking:
def setup_class(cls):
if sys.version_info < (2, 6):
@@ -15,25 +64,20 @@
def check_float(self, x):
# check roundtrip
- Q = ieee.float_pack(x, 8)
- y = ieee.float_unpack(Q, 8)
- assert repr(x) == repr(y), '%r != %r, Q=%r' % (x, y, Q)
+ for size in [10, 12, 16]:
+ for be in [False, True]:
+ Q = []
+ ieee.pack_float80(Q, x, size, be)
+ Q = Q[0]
+ y = ieee.unpack_float80(Q, be)
+ assert repr(x) == repr(y), '%r != %r, Q=%r' % (x, y, Q)
- Q = ieee.float_pack80(x)
- y = ieee.float_unpack80(Q)
- assert repr(x) == repr(y), '%r != %r, Q=%r' % (x, y, Q)
-
- Q = []
- ieee.pack_float(Q, x, 8, False)
- Q = Q[0]
- y = ieee.unpack_float(Q, False)
- assert repr(x) == repr(y), '%r != %r, Q=%r' % (x, y, Q)
-
- Q = []
- ieee.pack_float80(Q, x, False)
- Q = Q[0]
- y = ieee.unpack_float80(Q, False)
- assert repr(x) == repr(y), '%r != %r, Q=%r' % (x, y, Q)
+ for be in [False, True]:
+ Q = []
+ ieee.pack_float(Q, x, 8, be)
+ Q = Q[0]
+ y = ieee.unpack_float(Q, be)
+ assert repr(x) == repr(y), '%r != %r, Q=%r' % (x, y, Q)
# check that packing agrees with the struct module
struct_pack8 = struct.unpack('<Q', struct.pack('<d', x))[0]
@@ -74,26 +118,8 @@
self.check_float(0.0)
self.check_float(-0.0)
- def test_check_size(self):
- # these were refactored into separate pack80/unpack80 functions
- py.test.raises(ValueError, ieee.float_pack, 1.0, 12)
- py.test.raises(ValueError, ieee.float_pack, 1.0, 16)
- py.test.raises(ValueError, ieee.float_unpack, 1, 12)
- py.test.raises(ValueError, ieee.float_unpack, 1, 16)
-
def test_nans(self):
- Q = ieee.float_pack80(float('nan'))
- y = ieee.float_unpack80(Q)
- assert repr(y) == 'nan'
- Q = ieee.float_pack(float('nan'), 8)
- y = ieee.float_unpack(Q, 8)
- assert repr(y) == 'nan'
- L = ieee.float_pack(float('nan'), 4)
- z = ieee.float_unpack(L, 4)
- assert repr(z) == 'nan'
- L = ieee.float_pack(float('nan'), 2)
- z = ieee.float_unpack(L, 2)
- assert repr(z) == 'nan'
+ self.check_float(float('nan'))
def test_simple(self):
test_values = [1e-10, 0.00123, 0.5, 0.7, 1.0, 123.456, 1e10]
@@ -142,7 +168,7 @@
def test_random(self):
# construct a Python float from random integer, using struct
- for _ in xrange(100000):
+ for _ in xrange(10000):
Q = random.randrange(2**64)
x = struct.unpack('<d', struct.pack('<Q', Q))[0]
# nans are tricky: we can't hope to reproduce the bit
@@ -151,37 +177,6 @@
continue
self.check_float(x)
- def test_halffloat_exact(self):
- #testcases generated from numpy.float16(x).view('uint16')
- cases = [[0, 0], [10, 18688], [-10, 51456], [10e3, 28898],
- [float('inf'), 31744], [-float('inf'), 64512]]
- for c, h in cases:
- hbit = ieee.float_pack(c, 2)
- assert hbit == h
- assert c == ieee.float_unpack(h, 2)
-
- def test_halffloat_inexact(self):
- #testcases generated from numpy.float16(x).view('uint16')
- cases = [[10.001, 18688, 10.], [-10.001, 51456, -10],
- [0.027588, 10000, 0.027587890625],
- [22001, 30047, 22000]]
- for c, h, f in cases:
- hbit = ieee.float_pack(c, 2)
- assert hbit == h
- assert f == ieee.float_unpack(h, 2)
-
- def test_halffloat_overunderflow(self):
- import math
- cases = [[670000, float('inf')], [-67000, -float('inf')],
- [1e-08, 0], [-1e-8, -0.]]
- for f1, f2 in cases:
- try:
- f_out = ieee.float_unpack(ieee.float_pack(f1, 2), 2)
- except OverflowError:
- f_out = math.copysign(float('inf'), f1)
- assert f_out == f2
- assert math.copysign(1., f_out) == math.copysign(1., f2)
-
class TestCompiled:
def test_pack_float(self):
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit