[pypy-commit] cffi add-float-dotdotdot: Test the various cases, fix issues with rounding, etc.

arigo noreply at buildbot.pypy.org
Fri Aug 28 10:46:59 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: add-float-dotdotdot
Changeset: r2260:349afe35982d
Date: 2015-08-28 10:47 +0200
http://bitbucket.org/cffi/cffi/changeset/349afe35982d/

Log:	Test the various cases, fix issues with rounding, etc.

	A subtle point is that your version uses is_float_type(), which is
	true also for "long double", and so it will loose precision on
	platforms like x86 Linux where "long double" is larger than
	"double".

diff --git a/c/realize_c_type.c b/c/realize_c_type.c
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -166,6 +166,17 @@
                         "size (or not an integer type at all)");
         return NULL;
     }
+    else if (num == _CFFI__UNKNOWN_FLOAT_PRIM) {
+        PyErr_SetString(FFIError, "primitive floating-point type with an "
+                        "unexpected size (or not a float type at all)");
+        return NULL;
+    }
+    else if (num == _CFFI__UNKNOWN_LONG_DOUBLE) {
+        PyErr_SetString(FFIError, "primitive floating-point type is "
+                        "'long double', not supported for now with "
+                        "the syntax 'typedef double... xxx;'");
+        return NULL;
+    }
     else {
         PyErr_Format(PyExc_NotImplementedError, "prim=%d", num);
         return NULL;
diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -215,9 +215,10 @@
      _CFFI__UNKNOWN_PRIM)
 
 #define _cffi_prim_float(size)                                          \
-    ((size) == 4 ? _CFFI_PRIM_FLOAT :                                   \
-     (size) == 8 ? _CFFI_PRIM_DOUBLE :                                  \
-     _CFFI__UNKNOWN_PRIM)
+    ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT :                       \
+     (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE :                     \
+     (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE :       \
+     _CFFI__UNKNOWN_FLOAT_PRIM)
 
 #define _cffi_check_int(got, got_nonpos, expected)      \
     ((got_nonpos) == (expected <= 0) &&                 \
diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py
--- a/cffi/cffi_opcode.py
+++ b/cffi/cffi_opcode.py
@@ -106,7 +106,9 @@
 PRIM_UINTMAX       = 47
 
 _NUM_PRIM          = 48
-_UNKNOWN_PRIM      = -1
+_UNKNOWN_PRIM          = -1
+_UNKNOWN_FLOAT_PRIM    = -2
+_UNKNOWN_LONG_DOUBLE   = -3
 
 PRIMITIVE_TO_INDEX = {
     'char':               PRIM_CHAR,
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -649,14 +649,17 @@
         if len(typenames) == 1:
             return model.unknown_type(decl.name)
 
-        result = None
-        for t in typenames[:-1]:
-            if t in ('int', 'short', 'long', 'signed', 'unsigned', 'char'):
-                result = model.UnknownIntegerType(decl.name)
-            elif t in ('float', 'double'):
-                result = model.UnknownFloatType(decl.name)
-            else:
-                raise api.FFIError(':%d: bad usage of "..."' % decl.coord.line)
+        if (typenames[:-1] == ['float'] or
+            typenames[:-1] == ['double']):
+            # not for 'long double' so far
+            result = model.UnknownFloatType(decl.name)
+        else:
+            for t in typenames[:-1]:
+                if t not in ['int', 'short', 'long', 'signed',
+                             'unsigned', 'char']:
+                    raise api.FFIError(':%d: bad usage of "..."' %
+                                       decl.coord.line)
+            result = model.UnknownIntegerType(decl.name)
 
         if self._uses_new_feature is None:
             self._uses_new_feature = "'typedef %s... %s'" % (
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -35,9 +35,6 @@
     def is_integer_type(self):
         return False
 
-    def is_float_type(self):
-        return False
-
     def get_cached_btype(self, ffi, finishlist, can_delay=False):
         try:
             BType = ffi._cached_btypes[self]
@@ -161,7 +158,7 @@
         self.c_name_with_marker = name + '&'
 
     def is_integer_type(self):
-        return True    # for now
+        return True
 
     def build_backend_type(self, ffi, finishlist):
         raise NotImplementedError("integer type '%s' can only be used after "
@@ -174,9 +171,6 @@
         self.name = name
         self.c_name_with_marker = name + '&'
 
-    def is_float_type(self):
-        return True  # for now
-
     def build_backend_type(self, ffi, finishlist):
         raise NotImplementedError("float type '%s' can only be used after "
                                   "compilation" % self.name)
diff --git a/cffi/parse_c_type.h b/cffi/parse_c_type.h
--- a/cffi/parse_c_type.h
+++ b/cffi/parse_c_type.h
@@ -79,7 +79,9 @@
 #define _CFFI_PRIM_UINTMAX      47
 
 #define _CFFI__NUM_PRIM         48
-#define _CFFI__UNKNOWN_PRIM    (-1)
+#define _CFFI__UNKNOWN_PRIM           (-1)
+#define _CFFI__UNKNOWN_FLOAT_PRIM     (-2)
+#define _CFFI__UNKNOWN_LONG_DOUBLE    (-3)
 
 
 struct _cffi_global_s {
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -468,8 +468,10 @@
             if tp.is_integer_type() and tp.name != '_Bool':
                 converter = '_cffi_to_c_int'
                 extraarg = ', %s' % tp.name
-            elif tp.is_float_type():
-                converter = '_cffi_to_c_float'
+            elif isinstance(tp, model.UnknownFloatType):
+                # don't check with is_float_type(): it may be a 'long
+                # double' here, and _cffi_to_c_double would loose precision
+                converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
             else:
                 converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''),
                                                    tp.name.replace(' ', '_'))
@@ -524,8 +526,8 @@
         if isinstance(tp, model.BasePrimitiveType):
             if tp.is_integer_type():
                 return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
-            elif tp.is_float_type():
-                return '_cffi_from_c_double(%s)' % (var, )
+            elif isinstance(tp, model.UnknownFloatType):
+                return '_cffi_from_c_double(%s)' % (var,)
             elif tp.name != 'long double':
                 return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var)
             else:
@@ -1112,7 +1114,9 @@
         self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
 
     def _emit_bytecode_UnknownFloatType(self, tp, index):
-        s = '_cffi_prim_float(sizeof(%s))' % (tp.name, )
+        s = ('_cffi_prim_float(sizeof(%s) *\n'
+             '           (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
+             '         )' % (tp.name, tp.name))
         self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
 
     def _emit_bytecode_RawFunctionType(self, tp, index):
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1,6 +1,6 @@
 
 import sys, os, py
-from cffi import FFI, VerificationError
+from cffi import FFI, VerificationError, FFIError
 from cffi import recompiler
 from testing.udir import udir
 from testing.support import u
@@ -1057,12 +1057,51 @@
 
 def test_some_float_type():
     ffi = FFI()
-    ffi.cdef("typedef double... foo_t; foo_t sum(foo_t[]);")
+    ffi.cdef("""
+        typedef double... foo_t;
+        typedef float... bar_t;
+        foo_t sum(foo_t[]);
+        bar_t neg(bar_t);
+        """)
     lib = verify(ffi, 'test_some_float_type', """
         typedef float foo_t;
         static foo_t sum(foo_t x[]) { return x[0] + x[1]; }
+        typedef double bar_t;
+        static double neg(double x) { return -x; }
     """)
     assert lib.sum([40.0, 2.25]) == 42.25
+    assert lib.sum([12.3, 45.6]) != 12.3 + 45.6     # precision loss
+    assert lib.neg(12.3) == -12.3                   # no precision loss
+
+def test_some_float_invalid_1():
+    ffi = FFI()
+    py.test.raises(FFIError, ffi.cdef, "typedef long double... foo_t;")
+
+def test_some_float_invalid_2():
+    ffi = FFI()
+    ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);")
+    lib = verify(ffi, 'test_some_float_invalid_2', """
+        typedef unsigned long foo_t;
+        foo_t neg(foo_t x) { return -x; }
+    """)
+    e = py.test.raises(ffi.error, getattr, lib, 'neg')
+    assert str(e.value) == ("primitive floating-point type with an unexpected "
+                            "size (or not a float type at all)")
+
+def test_some_float_invalid_3():
+    ffi = FFI()
+    ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);")
+    lib = verify(ffi, 'test_some_float_invalid_3', """
+        typedef long double foo_t;
+        foo_t neg(foo_t x) { return -x; }
+    """)
+    if ffi.sizeof("long double") == ffi.sizeof("double"):
+        assert lib.neg(12.3) == -12.3
+    else:
+        e = py.test.raises(ffi.error, getattr, lib, 'neg')
+        assert str(e.value) == ("primitive floating-point type is "
+                                "'long double', not supported for now with "
+                                "the syntax 'typedef double... xxx;'")
 
 def test_issue200():
     ffi = FFI()


More information about the pypy-commit mailing list