[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