Author: mattip <[email protected]>
Branch: numpypy.float16
Changeset: r58667:6f2eb14406b3
Date: 2012-10-30 02:27 +0200
http://bitbucket.org/pypy/pypy/changeset/6f2eb14406b3/
Log: add halffloat bit conversion, tests
diff --git a/pypy/module/micronumpy/halffloat.py
b/pypy/module/micronumpy/halffloat.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/halffloat.py
@@ -0,0 +1,110 @@
+# Based on numpy's halffloat.c, this is an implementation of routines
+# for 16 bit float values.
+
+#from pypy.rpython.lltypesystem import rffi
+#from rffi import USHORT as uint16
+#def tofloat(x):
+# return rffi.cast(rffi.FLOAT, x)
+
+'''
+def half_to_float(x):
+ assert isinstance(x, float16)
+ xbits = x.view(uint16)
+ fbits = halffloatbits_to_floatbits(xbits)
+ return uint32(fbits).view(float32)
+
+def float_to_half(f):
+ assert isinstance(f, (float32, float))
+ fbits = float32(f).view(uint32)
+ xbits = floatbits_to_halfbits(fbits)
+ return uint16(xbits).view(float16)
+'''
+def halfbits_to_floatbits(x):
+ h_exp = x & 0x7c00
+ f_sign = (x & 0x8000) << 16
+ if h_exp == 0: #0 or subnormal
+ h_sig = x & 0x03ff
+ if h_sig == 0:
+ return f_sign
+ #Subnormal
+ h_sig <<= 1;
+ while (h_sig & 0x0400) == 0:
+ h_sig <<= 1
+ h_exp += 1
+ f_exp = 127 - 15 - h_exp << 23
+ f_sig = h_sig & 0x03ff << 13
+ return f_sign & f_exp & f_sig
+ elif h_exp == 0x7c00: # inf or nan
+ return f_sign + 0x7f800000 + ((x & 0x03ff) << 13)
+ # Just need to adjust the exponent and shift
+ return f_sign + (((x & 0x7fff) + 0x1c000) << 13)
+
+
+def floatbits_to_halfbits(f):
+ h_sgn = (f & 0x80000000) >> 16
+ f_exp = f & 0x7f800000
+ if f_exp >= 0x47800000:
+ # Exponent overflow, convert to signed inf/nan
+ if f_exp == 0x7f800000:
+ # inf or nan
+ f_sig = f & 0x007fffff
+ if f_sig != 0:
+ #nan - propagate the flag in the significand
+ ret = 0x7c00 + (f_sig >> 13)
+ # ... but make sure it stays a nan
+ if ret == 0x7c00:
+ ret += 1
+ return h_sgn + ret
+ else:
+ # signed inf
+ return h_sgn + 0x7c00
+ else:
+ # overflow to signed inf
+ # npy_set_floatstatus_overflow()
+ return h_sgn + 0x7c00
+ if f_exp <= 0x38000000:
+ # Exponent underflow converts to a subnormal half or signed zero
+ if f_exp < 0x33000000:
+ # Signed zeros, subnormal floats, and floats with small
+ # exponents all conver to signed zero halfs
+ if f & 0x7fffffff != 0:
+ pass
+ # npy_set_floatstatus_underflow()
+ return h_sgn
+ # Make the subnormal significand
+ f_exp >>= 23
+ f_sig = 0x00800000 + (f & 0x007fffff)
+ if (f_sig & ((1 << (126 - f_exp)) -1)) != 0:
+ # not exactly represented, therefore underflowed
+ pass
+ # npy_set_floatstatus_underflow()
+ f_sig >>= (113 - f_exp)
+ # Handle rounding by adding 1 to the bit beyond half precision
+
+ if (f_sig & 0x00003fff) != 0x00001000:
+ # The last remaining bit is 1, and the rmaining bit pattern
+ # is not 1000...0, so round up
+ f_sig += 0x00001000
+ h_sig = f_sig >> 13
+ # If the rounding cuased a bit to spill into h_exp, it will
+ # increment h_exp from zero to one and h_sig will remain zero
+ # which is the correct result.
+ return h_sgn + h_sig
+ # No overflow or underflow
+ h_exp = (f_exp - 0x38000000)>> 13
+ f_sig = f & 0x007fffff
+ if (f_sig & 0x00003fff) != 0x00001000:
+ # The last remaining bit is 1, and the rmaining bit pattern
+ # is not 1000...0, so round up
+ f_sig += 0x00001000
+ h_sig = f_sig >> 13
+ # If the rounding cuased a bit to spill into h_exp, it will
+ # increment h_exp from zero to one and h_sig will remain zero
+ # which is the correct result. However, h_exp may increment to
+ # 15, at greatest, in which case the result overflows
+ h_sig += h_exp
+ if h_sig == 0x7c00:
+ pass
+ #npy_set_floatstatus_overflow()
+ return h_sgn + h_sig
+
diff --git a/pypy/module/micronumpy/interp_dtype.py
b/pypy/module/micronumpy/interp_dtype.py
--- a/pypy/module/micronumpy/interp_dtype.py
+++ b/pypy/module/micronumpy/interp_dtype.py
@@ -467,7 +467,7 @@
)
self.w_float16dtype = W_Dtype(
types.Float16(),
- num=11,
+ num=23,
kind=FLOATINGLTR,
name="float16",
char="e",
diff --git a/pypy/module/micronumpy/test/test_halffloat.py
b/pypy/module/micronumpy/test/test_halffloat.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/micronumpy/test/test_halffloat.py
@@ -0,0 +1,34 @@
+
+from pypy.module.micronumpy.test.test_base import BaseNumpyAppTest
+
+class AppTestUfuncs(BaseNumpyAppTest):
+ def setup_class(cls):
+ BaseNumpyAppTest.setup_class.im_func(cls)
+ from pypy.module.micronumpy import halffloat
+ cls.w_halffloat = cls.space.wrap(halffloat)
+
+ def test_bitconvert_exact(self):
+ #from _numpypy import array, uint32
+ # These test cases were created by
+ # numpy.float16(v).view(uint16)
+ # numpy.float32(v).view(uint32)
+ cases = [[0., 0, 0], [10, 1092616192, 18688], [-10, 3240099840,
51456],
+ [10e3, 1176256512, 28898], [float('inf'), 2139095040, 31744],
+ [-float('inf'), 4286578688, 64512]]
+ for v, fbits, hbits in cases:
+ # No 'view' in numpypy yet
+ # fbits = array(v, dtype='float32').view(uint32)
+ f = self.halffloat.floatbits_to_halfbits(fbits)
+ assert [f, v] == [hbits, v]
+ f = self.halffloat.halfbits_to_floatbits(hbits)
+ assert [f, v] == [fbits, v]
+ def test_bitconvert_inexact(self):
+ cases = [[10.001, 1092617241, 1092616192, 18688],
+ [-10.001, 3240100889, 3240099840, 51456],
+ [22001.0, 1185669632, 1185669120, 30047],]
+ for v, fexact, finexact, hbits in cases:
+ f = self.halffloat.floatbits_to_halfbits(fexact)
+ assert [f, v] == [hbits, v]
+ f = self.halffloat.halfbits_to_floatbits(hbits)
+ assert [f, v] == [finexact, v]
+
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
@@ -18,6 +18,7 @@
from pypy.rlib import jit
from pypy.rlib.rstring import StringBuilder
+from pypy.module.micronumpy import halffloat
degToRad = math.pi / 180.0
log2 = math.log(2)
@@ -938,20 +939,19 @@
return self.box(-1.0)
@specialize.argtype(1)
- def box(self, value):
- return self.BoxType(
- rffi.cast(self._COMPUTATION_T, value))
-
def unbox(self, box):
assert isinstance(box, self.BoxType)
- return box.tofloat()
+ return box.value
- def store(self, arr, i, offset, box):
- raw_storage_setitem(arr.storage, i+offset, box.tobytes())
+ @specialize.argtype(1)
+ def box(self, value):
+ return self.BoxType(rffi.cast(self._COMPUTATION_T, value))
+
def _read(self, storage, i, offset):
byte_rep = raw_storage_getitem(self.T, storage, i + offset)
- return self.BoxType.val_to_float(byte_rep)
+ fbits = halffloat.halfbits_to_floatbits(byte_rep)
+ return rffi.cast(rffi.FLOAT, fbits)
def read(self, arr, i, offset, dtype=None):
val = self._read(arr.storage, i, offset)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit