Skip to content

Commit 6d2c96e

Browse files
authored
Add PyFloat_Pack8() (#26)
1 parent adca68c commit 6d2c96e

File tree

4 files changed

+137
-0
lines changed

4 files changed

+137
-0
lines changed

docs/api.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,42 @@ Python 3.11
3939
4040
Not available on PyPy
4141
42+
.. c:function:: int PyFloat_Pack2(double x, unsigned char *p, int le)
43+
44+
Pack a C double as the IEEE 754 binary16 half-precision format.
45+
46+
Availability: Python 3.6 and newer. Not available on PyPy
47+
48+
.. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le)
49+
50+
Pack a C double as the IEEE 754 binary32 single precision format.
51+
52+
Not available on PyPy
53+
54+
.. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le)
55+
56+
Pack a C double as the IEEE 754 binary64 double precision format.
57+
58+
Not available on PyPy
59+
60+
.. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le)
61+
62+
Unpack the IEEE 754 binary16 half-precision format as a C double.
63+
64+
Availability: Python 3.6 and newer. Not available on PyPy
65+
66+
.. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le)
67+
68+
Unpack the IEEE 754 binary32 single precision format as a C double.
69+
70+
Not available on PyPy
71+
72+
.. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le)
73+
74+
Unpack the IEEE 754 binary64 double precision format as a C double.
75+
76+
Not available on PyPy
77+
4278
Python 3.10
4379
-----------
4480

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Changelog
22
=========
33

4+
* 2022-03-12: Add functions ``PyFloat_Pack2()``, ``PyFloat_Pack4()``,
5+
``PyFloat_Pack8()``, ``PyFloat_Unpack2()``, ``PyFloat_Unpack4()`` and
6+
``PyFloat_Unpack8()``.
47
* 2022-03-03: The project moved to https://github.com/python/pythoncapi_compat
58
* 2022-02-11: The project license changes from the MIT license to the Zero
69
Clause BSD (0BSD) license. Projects copying ``pythoncapi_compat.h`` no longer

pythoncapi_compat.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,45 @@ _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
391391
#endif
392392

393393

394+
// bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7.
395+
// bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1.
396+
// Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal
397+
// C API: Python 3.11a2-3.11a6 versions are not supported.
398+
#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)
399+
PYCAPI_COMPAT_STATIC_INLINE(int)
400+
PyFloat_Pack2(double x, char *p, int le)
401+
{ return _PyFloat_Pack2(x, (unsigned char*)p, le); }
402+
403+
PYCAPI_COMPAT_STATIC_INLINE(double)
404+
PyFloat_Unpack2(const char *p, int le)
405+
{ return _PyFloat_Unpack2((const unsigned char *)p, le); }
406+
#endif
407+
408+
409+
// bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and
410+
// PyFloat_Unpack8() to Python 3.11a7.
411+
// Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4()
412+
// and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions
413+
// are not supported.
414+
#if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION)
415+
PYCAPI_COMPAT_STATIC_INLINE(int)
416+
PyFloat_Pack4(double x, char *p, int le)
417+
{ return _PyFloat_Pack4(x, (unsigned char*)p, le); }
418+
419+
PYCAPI_COMPAT_STATIC_INLINE(int)
420+
PyFloat_Pack8(double x, char *p, int le)
421+
{ return _PyFloat_Pack8(x, (unsigned char*)p, le); }
422+
423+
PYCAPI_COMPAT_STATIC_INLINE(double)
424+
PyFloat_Unpack4(const char *p, int le)
425+
{ return _PyFloat_Unpack4((const unsigned char *)p, le); }
426+
427+
PYCAPI_COMPAT_STATIC_INLINE(double)
428+
PyFloat_Unpack8(const char *p, int le)
429+
{ return _PyFloat_Unpack8((const unsigned char *)p, le); }
430+
#endif
431+
432+
394433
// Py_UNUSED() was added to Python 3.4.0b2.
395434
#if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED)
396435
# if defined(__GNUC__) || defined(__clang__)

tests/test_pythoncapi_compat_cext.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,62 @@ test_module(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
406406
}
407407

408408

409+
#if (PY_VERSION_HEX <= 0x030B00A1 || 0x030B00A7 <= PY_VERSION_HEX) && !defined(PYPY_VERSION)
410+
static PyObject *
411+
test_float_pack(PyObject *Py_UNUSED(module), PyObject* Py_UNUSED(ignored))
412+
{
413+
const int big_endian = 0;
414+
const int little_endian = 1;
415+
char data[8];
416+
const double d = 1.5;
417+
418+
#if PY_VERSION_HEX >= 0x030600B1
419+
# define HAVE_FLOAT_PACK2
420+
#endif
421+
422+
// Test Pack (big endian)
423+
#ifdef HAVE_FLOAT_PACK2
424+
assert(PyFloat_Pack2(d, data, big_endian) == 0);
425+
assert(memcmp(data, ">\x00", 2) == 0);
426+
#endif
427+
428+
assert(PyFloat_Pack4(d, data, big_endian) == 0);
429+
assert(memcmp(data, "?\xc0\x00\x00", 2) == 0);
430+
431+
assert(PyFloat_Pack8(d, data, big_endian) == 0);
432+
assert(memcmp(data, "?\xf8\x00\x00\x00\x00\x00\x00", 2) == 0);
433+
434+
// Test Pack (little endian)
435+
#ifdef HAVE_FLOAT_PACK2
436+
assert(PyFloat_Pack2(d, data, little_endian) == 0);
437+
assert(memcmp(data, "\x00>", 2) == 0);
438+
#endif
439+
440+
assert(PyFloat_Pack4(d, data, little_endian) == 0);
441+
assert(memcmp(data, "\x00\x00\xc0?", 2) == 0);
442+
443+
assert(PyFloat_Pack8(d, data, little_endian) == 0);
444+
assert(memcmp(data, "\x00\x00\x00\x00\x00\x00\xf8?", 2) == 0);
445+
446+
// Test Unpack (big endian)
447+
#ifdef HAVE_FLOAT_PACK2
448+
assert(PyFloat_Unpack2(">\x00", big_endian) == d);
449+
#endif
450+
assert(PyFloat_Unpack4("?\xc0\x00\x00", big_endian) == d);
451+
assert(PyFloat_Unpack8("?\xf8\x00\x00\x00\x00\x00\x00", big_endian) == d);
452+
453+
// Test Unpack (little endian)
454+
#ifdef HAVE_FLOAT_PACK2
455+
assert(PyFloat_Unpack2("\x00>", little_endian) == d);
456+
#endif
457+
assert(PyFloat_Unpack4("\x00\x00\xc0?", little_endian) == d);
458+
assert(PyFloat_Unpack8("\x00\x00\x00\x00\x00\x00\xf8?", little_endian) == d);
459+
460+
Py_RETURN_NONE;
461+
}
462+
#endif
463+
464+
409465
static struct PyMethodDef methods[] = {
410466
{"test_object", test_object, METH_NOARGS, NULL},
411467
{"test_py_is", test_py_is, METH_NOARGS, NULL},
@@ -418,6 +474,9 @@ static struct PyMethodDef methods[] = {
418474
{"test_calls", test_calls, METH_NOARGS, NULL},
419475
{"test_gc", test_gc, METH_NOARGS, NULL},
420476
{"test_module", test_module, METH_NOARGS, NULL},
477+
#if (PY_VERSION_HEX <= 0x030B00A1 || 0x030B00A7 <= PY_VERSION_HEX) && !defined(PYPY_VERSION)
478+
{"test_float_pack", test_float_pack, METH_NOARGS, NULL},
479+
#endif
421480
{NULL, NULL, 0, NULL}
422481
};
423482

0 commit comments

Comments
 (0)