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

Reply via email to