Author: Armin Rigo <ar...@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 ------------------- _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit