diff --git a/docs/api.rst b/docs/api.rst index fbba6b6..bb3fe18 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -39,6 +39,42 @@ Python 3.11 Not available on PyPy +.. c:function:: int PyFloat_Pack2(double x, unsigned char *p, int le) + + Pack a C double as the IEEE 754 binary16 half-precision format. + + Availability: Python 3.6 and newer. Not available on PyPy + +.. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le) + + Pack a C double as the IEEE 754 binary32 single precision format. + + Not available on PyPy + +.. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le) + + Pack a C double as the IEEE 754 binary64 double precision format. + + Not available on PyPy + +.. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le) + + Unpack the IEEE 754 binary16 half-precision format as a C double. + + Availability: Python 3.6 and newer. Not available on PyPy + +.. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le) + + Unpack the IEEE 754 binary32 single precision format as a C double. + + Not available on PyPy + +.. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le) + + Unpack the IEEE 754 binary64 double precision format as a C double. + + Not available on PyPy + Python 3.10 ----------- diff --git a/docs/changelog.rst b/docs/changelog.rst index eed780e..9253e76 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,9 @@ Changelog ========= +* 2022-03-12: Add functions ``PyFloat_Pack2()``, ``PyFloat_Pack4()``, + ``PyFloat_Pack8()``, ``PyFloat_Unpack2()``, ``PyFloat_Unpack4()`` and + ``PyFloat_Unpack8()``. * 2022-03-03: The project moved to https://github.com/python/pythoncapi_compat * 2022-02-11: The project license changes from the MIT license to the Zero Clause BSD (0BSD) license. Projects copying ``pythoncapi_compat.h`` no longer diff --git a/pythoncapi_compat.h b/pythoncapi_compat.h index d035456..2837467 100644 --- a/pythoncapi_compat.h +++ b/pythoncapi_compat.h @@ -391,6 +391,45 @@ _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) { #endif +// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. +// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. +// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal +// C API: Python 3.11a2-3.11a6 versions are not supported. +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFloat_Pack2(double x, char *p, int le) +{ return _PyFloat_Pack2(x, (unsigned char*)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(double) +PyFloat_Unpack2(const char *p, int le) +{ return _PyFloat_Unpack2((const unsigned char *)p, le); } +#endif + + +// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and +// PyFloat_Unpack8() to Python 3.11a7. +// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() +// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions +// are not supported. +#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFloat_Pack4(double x, char *p, int le) +{ return _PyFloat_Pack4(x, (unsigned char*)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(int) +PyFloat_Pack8(double x, char *p, int le) +{ return _PyFloat_Pack8(x, (unsigned char*)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(double) +PyFloat_Unpack4(const char *p, int le) +{ return _PyFloat_Unpack4((const unsigned char *)p, le); } + +PYCAPI_COMPAT_STATIC_INLINE(double) +PyFloat_Unpack8(const char *p, int le) +{ return _PyFloat_Unpack8((const unsigned char *)p, le); } +#endif + + // Py_UNUSED() was added to Python 3.4.0b2. #if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) # if defined(__GNUC__) || defined(__clang__) diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index d6cf877..fa1483c 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -406,6 +406,62 @@ test_module(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) } +#if (PY_VERSION_HEX <= 0x030B00A1 || 0x030B00A7 <= PY_VERSION_HEX) && !defined(PYPY_VERSION) +static PyObject * +test_float_pack(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored)) +{ + const int big_endian = 0; + const int little_endian = 1; + char data[8]; + const double d = 1.5; + +#if PY_VERSION_HEX >= 0x030600B1 +# define HAVE_FLOAT_PACK2 +#endif + + // Test Pack (big endian) +#ifdef HAVE_FLOAT_PACK2 + assert(PyFloat_Pack2(d, data, big_endian) == 0); + assert(memcmp(data, ">\x00", 2) == 0); +#endif + + assert(PyFloat_Pack4(d, data, big_endian) == 0); + assert(memcmp(data, "?\xc0\x00\x00", 2) == 0); + + assert(PyFloat_Pack8(d, data, big_endian) == 0); + assert(memcmp(data, "?\xf8\x00\x00\x00\x00\x00\x00", 2) == 0); + + // Test Pack (little endian) +#ifdef HAVE_FLOAT_PACK2 + assert(PyFloat_Pack2(d, data, little_endian) == 0); + assert(memcmp(data, "\x00>", 2) == 0); +#endif + + assert(PyFloat_Pack4(d, data, little_endian) == 0); + assert(memcmp(data, "\x00\x00\xc0?", 2) == 0); + + assert(PyFloat_Pack8(d, data, little_endian) == 0); + assert(memcmp(data, "\x00\x00\x00\x00\x00\x00\xf8?", 2) == 0); + + // Test Unpack (big endian) +#ifdef HAVE_FLOAT_PACK2 + assert(PyFloat_Unpack2(">\x00", big_endian) == d); +#endif + assert(PyFloat_Unpack4("?\xc0\x00\x00", big_endian) == d); + assert(PyFloat_Unpack8("?\xf8\x00\x00\x00\x00\x00\x00", big_endian) == d); + + // Test Unpack (little endian) +#ifdef HAVE_FLOAT_PACK2 + assert(PyFloat_Unpack2("\x00>", little_endian) == d); +#endif + assert(PyFloat_Unpack4("\x00\x00\xc0?", little_endian) == d); + assert(PyFloat_Unpack8("\x00\x00\x00\x00\x00\x00\xf8?", little_endian) == d); + + Py_RETURN_NONE; +} +#endif + + static struct PyMethodDef methods[] = { {"test_object", test_object, METH_NOARGS, NULL}, {"test_py_is", test_py_is, METH_NOARGS, NULL}, @@ -418,6 +474,9 @@ static struct PyMethodDef methods[] = { {"test_calls", test_calls, METH_NOARGS, NULL}, {"test_gc", test_gc, METH_NOARGS, NULL}, {"test_module", test_module, METH_NOARGS, NULL}, +#if (PY_VERSION_HEX <= 0x030B00A1 || 0x030B00A7 <= PY_VERSION_HEX) && !defined(PYPY_VERSION) + {"test_float_pack", test_float_pack, METH_NOARGS, NULL}, +#endif {NULL, NULL, 0, NULL} };