[pypy-commit] cffi default: Implement and document "long double".

arigo noreply at buildbot.pypy.org
Wed Aug 1 11:37:26 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r754:2c1afe72d34d
Date: 2012-08-01 11:37 +0200
http://bitbucket.org/cffi/cffi/changeset/2c1afe72d34d/

Log:	Implement and document "long double".

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -28,7 +28,7 @@
 #define CT_PRIMITIVE_SIGNED   1    /* signed integer */
 #define CT_PRIMITIVE_UNSIGNED 2    /* unsigned integer */
 #define CT_PRIMITIVE_CHAR     4    /* char, wchar_t */
-#define CT_PRIMITIVE_FLOAT    8    /* float, double */
+#define CT_PRIMITIVE_FLOAT    8    /* float, double, long double */
 #define CT_POINTER           16    /* pointer, excluding ptr-to-func */
 #define CT_ARRAY             32    /* array */
 #define CT_STRUCT            64    /* struct */
@@ -43,6 +43,7 @@
 #define CT_IS_ENUM               8192
 #define CT_IS_PTR_TO_OWNED      16384
 #define CT_CUSTOM_FIELD_POS     32768
+#define CT_IS_LONGDOUBLE        65536
 #define CT_PRIMITIVE_ANY  (CT_PRIMITIVE_SIGNED |        \
                            CT_PRIMITIVE_UNSIGNED |      \
                            CT_PRIMITIVE_CHAR |          \
@@ -104,6 +105,7 @@
     unsigned long long m_longlong;
     float m_float;
     double m_double;
+    long double m_longdouble;
 } union_alignment;
 
 typedef struct {
@@ -505,6 +507,12 @@
     }
 }
 
+static long double
+read_raw_longdouble_data(char *target)
+{
+    return *((long double*)target);
+}
+
 static void
 write_raw_float_data(char *target, double source, int size)
 {
@@ -516,6 +524,12 @@
         Py_FatalError("write_raw_float_data: bad float size");
 }
 
+static void
+write_raw_longdouble_data(char *target, long double source)
+{
+    *((long double*)target) = source;
+}
+
 static PyObject *
 new_simple_cdata(char *data, CTypeDescrObject *ct)
 {
@@ -555,6 +569,8 @@
     return d_value;
 }
 
+static CDataObject *_new_casted_primitive(CTypeDescrObject *ct);  /*forward*/
+
 static PyObject *
 convert_to_object(char *data, CTypeDescrObject *ct)
 {
@@ -603,8 +619,17 @@
             return PyLong_FromUnsignedLongLong(value);
     }
     else if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
-        double value = read_raw_float_data(data, ct->ct_size);
-        return PyFloat_FromDouble(value);
+        if (!(ct->ct_flags & CT_IS_LONGDOUBLE)) {
+            double value = read_raw_float_data(data, ct->ct_size);
+            return PyFloat_FromDouble(value);
+        }
+        else {
+            long double value = read_raw_longdouble_data(data);
+            CDataObject *cd = _new_casted_primitive(ct);
+            if (cd != NULL)
+                write_raw_longdouble_data(cd->c_data, value);
+            return (PyObject *)cd;
+        }
     }
     else if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
         if (ct->ct_size == sizeof(char))
@@ -893,10 +918,22 @@
         return 0;
     }
     if (ct->ct_flags & CT_PRIMITIVE_FLOAT) {
-        double value = PyFloat_AsDouble(init);
+        double value;
+        if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                CData_Check(init) &&
+                (((CDataObject *)init)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            long double lvalue;
+            lvalue = read_raw_longdouble_data(((CDataObject *)init)->c_data);
+            write_raw_longdouble_data(data, lvalue);
+            return 0;
+        }
+        value = PyFloat_AsDouble(init);
         if (value == -1.0 && PyErr_Occurred())
             return -1;
-        write_raw_float_data(data, value, ct->ct_size);
+        if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+            write_raw_float_data(data, value, ct->ct_size);
+        else
+            write_raw_longdouble_data(data, (long double)value);
         return 0;
     }
     if (ct->ct_flags & CT_PRIMITIVE_CHAR) {
@@ -1114,20 +1151,32 @@
     return 0;
 }
 
+static PyObject *cdata_float(CDataObject *cd);  /*forward*/
+
 static PyObject *cdata_repr(CDataObject *cd)
 {
     char *p, *extra;
     PyObject *result, *s = NULL;
 
     if (cd->c_type->ct_flags & CT_PRIMITIVE_ANY) {
-        PyObject *o = convert_to_object(cd->c_data, cd->c_type);
-        if (o == NULL)
-            return NULL;
-        s = PyObject_Repr(o);
-        Py_DECREF(o);
-        if (s == NULL)
-            return NULL;
-        p = PyString_AS_STRING(s);
+        if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+            if (o == NULL)
+                return NULL;
+            s = PyObject_Repr(o);
+            Py_DECREF(o);
+            if (s == NULL)
+                return NULL;
+            p = PyString_AS_STRING(s);
+        }
+        else {
+            long double lvalue = read_raw_longdouble_data(cd->c_data);
+            s = PyString_FromStringAndSize(NULL, 128);   /* big enough */
+            if (s == NULL)
+                return NULL;
+            p = PyString_AS_STRING(s);
+            sprintf(p, "%LE", lvalue);
+        }
     }
     else {
         if (cd->c_data != NULL) {
@@ -1294,7 +1343,7 @@
 #endif
     }
     else if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
-        PyObject *o = convert_to_object(cd->c_data, cd->c_type);
+        PyObject *o = cdata_float(cd);
         PyObject *r = o ? PyNumber_Int(o) : NULL;
         Py_XDECREF(o);
         return r;
@@ -1318,7 +1367,14 @@
 static PyObject *cdata_float(CDataObject *cd)
 {
     if (cd->c_type->ct_flags & CT_PRIMITIVE_FLOAT) {
-        return convert_to_object(cd->c_data, cd->c_type);
+        double value;
+        if (!(cd->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            value = read_raw_float_data(cd->c_data, cd->c_type->ct_size);
+        }
+        else {
+            value = (double)read_raw_longdouble_data(cd->c_data);
+        }
+        return PyFloat_FromDouble(value);
     }
     PyErr_Format(PyExc_TypeError, "float() not supported on cdata '%s'",
                  cd->c_type->ct_name);
@@ -2318,6 +2374,16 @@
             }
             value = (unsigned char)PyString_AS_STRING(io)[0];
         }
+        else if ((ct->ct_flags & CT_IS_LONGDOUBLE) &&
+                 CData_Check(io) &&
+                 (((CDataObject *)io)->c_type->ct_flags & CT_IS_LONGDOUBLE)) {
+            long double lvalue;
+            lvalue = read_raw_longdouble_data(((CDataObject *)io)->c_data);
+            cd = _new_casted_primitive(ct);
+            if (cd != NULL)
+                write_raw_longdouble_data(cd->c_data, lvalue);
+            return (PyObject *)cd;
+        }
         else {
             value = PyFloat_AsDouble(io);
         }
@@ -2326,8 +2392,12 @@
             return NULL;
 
         cd = _new_casted_primitive(ct);
-        if (cd != NULL)
-            write_raw_float_data(cd->c_data, value, ct->ct_size);
+        if (cd != NULL) {
+            if (!(ct->ct_flags & CT_IS_LONGDOUBLE))
+                write_raw_float_data(cd->c_data, value, ct->ct_size);
+            else
+                write_raw_longdouble_data(cd->c_data, (long double)value);
+        }
         return (PyObject *)cd;
     }
     else {
@@ -2569,7 +2639,8 @@
        EPTYPE(ul, unsigned long, CT_PRIMITIVE_UNSIGNED )        \
        EPTYPE(ull, unsigned long long, CT_PRIMITIVE_UNSIGNED )  \
        EPTYPE(f, float, CT_PRIMITIVE_FLOAT )                    \
-       EPTYPE(d, double, CT_PRIMITIVE_FLOAT )
+       EPTYPE(d, double, CT_PRIMITIVE_FLOAT )                   \
+       EPTYPE(ld, long double, CT_PRIMITIVE_FLOAT | CT_IS_LONGDOUBLE )
 #ifdef HAVE_WCHAR_H
 # define ENUM_PRIMITIVE_TYPES_WCHAR                             \
        EPTYPE(wc, wchar_t, CT_PRIMITIVE_CHAR )
@@ -2635,6 +2706,8 @@
             ffitype = &ffi_type_float;
         else if (strcmp(ptypes->name, "double") == 0)
             ffitype = &ffi_type_double;
+        else if (strcmp(ptypes->name, "long double") == 0)
+            ffitype = &ffi_type_longdouble;
         else
             goto bad_ffi_type;
     }
@@ -3994,6 +4067,11 @@
     return ptr->a1 + (int)ptr->a2;
 }
 
+static long double _testfunc19(long double x)
+{
+    return x + x;
+}
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
@@ -4021,6 +4099,7 @@
     case 16: f = &_testfunc16; break;
     case 17: f = &_testfunc17; break;
     case 18: f = &_testfunc18; break;
+    case 19: f = &_testfunc19; break;
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1743,3 +1743,33 @@
     assert x[0] == 12.5
     x = cast(BFloat, cast(BDouble, 12.5))
     assert float(x) == 12.5
+
+def test_longdouble():
+    BLongDouble = new_primitive_type("long double")
+    BLongDoublePtr = new_pointer_type(BLongDouble)
+    BLongDoubleArray = new_array_type(BLongDoublePtr, None)
+    a = newp(BLongDoubleArray, 1)
+    x = a[0]
+    assert repr(x).startswith("<cdata 'long double' 0.0")
+    assert float(x) == 0.0
+    assert int(x) == 0
+    #
+    b = newp(BLongDoubleArray, [1.23])
+    x = b[0]
+    assert repr(x).startswith("<cdata 'long double' 1.23")
+    assert float(x) == 1.23
+    assert int(x) == 1
+    #
+    BFunc19 = new_function_type((BLongDouble,), BLongDouble)
+    f = cast(BFunc19, _testfunc(19))
+    start = 1
+    for i in range(2999):
+        start = f(start)
+    if sizeof(BLongDouble) > sizeof(new_primitive_type("double")):
+        assert repr(start).startswith("<cdata 'long double' 6.15")
+        assert repr(start).endswith("E+902>")
+        #
+        c = newp(BLongDoubleArray, [start])
+        x = c[0]
+        assert repr(x).endswith("E+902>")
+        assert float(x) == float("inf")
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -345,7 +345,7 @@
 
 * char, short, int, long, long long (both signed and unsigned)
 
-* float, double
+* float, double, long double
 
 * intN_t, uintN_t (for N=8,16,32,64), intptr_t, uintptr_t, ptrdiff_t,
   size_t, ssize_t
@@ -905,6 +905,11 @@
 |  ``float``,   | a float or anything on | a Python float   | float(), int() |
 |  ``double``   | which float() works    |                  |                |
 +---------------+------------------------+------------------+----------------+
+|``long double``| another <cdata> with   | a <cdata>, to    | float(), int() |
+|               | a ``long double``, or  | avoid loosing    |                |
+|               | anything on which      | precision (***)  |                |
+|               | float() works          |                  |                |
++---------------+------------------------+------------------+----------------+
 |  pointers     | another <cdata> with   | a <cdata>        | ``[]``, ``+``, |
 |               | a compatible type (i.e.|                  | ``-``          |
 |               | same type or ``char*`` |                  |                |
@@ -977,6 +982,13 @@
 .. versionchanged:: 0.3
    (**) C function calls are now done with the GIL released.
 
+.. versionadded:: 0.3
+   (***) ``long double`` is passed around in a cdata object to avoid loosing
+   precision, because a normal Python floating-point number only contains
+   enough precision for a ``double``.  If you want to operate on such numbers
+   without any precision loss, you need to define and use a family of C
+   functions like ``long double add(long double a, long double b);``.
+
 
 Reference: verifier
 -------------------


More information about the pypy-commit mailing list