mirror of https://github.com/python/cpython
bpo-46906: Add PyFloat_Pack8() to the C API (GH-31657)
Add new functions to pack and unpack C double (serialize and deserialize): * PyFloat_Pack2(), PyFloat_Pack4(), PyFloat_Pack8() * PyFloat_Unpack2(), PyFloat_Unpack4(), PyFloat_Unpack8() Document these functions and add unit tests. Rename private functions and move them from the internal C API to the public C API: * _PyFloat_Pack2() => PyFloat_Pack2() * _PyFloat_Pack4() => PyFloat_Pack4() * _PyFloat_Pack8() => PyFloat_Pack8() * _PyFloat_Unpack2() => PyFloat_Unpack2() * _PyFloat_Unpack4() => PyFloat_Unpack4() * _PyFloat_Unpack8() => PyFloat_Unpack8() Replace the "unsigned char*" type with "char*" which is more common and easy to use.
This commit is contained in:
parent
ecfff63e06
commit
882d8096c2
|
@ -76,3 +76,85 @@ Floating Point Objects
|
|||
.. c:function:: double PyFloat_GetMin()
|
||||
|
||||
Return the minimum normalized positive float *DBL_MIN* as C :c:type:`double`.
|
||||
|
||||
|
||||
Pack and Unpack functions
|
||||
=========================
|
||||
|
||||
The pack and unpack functions provide an efficient platform-independent way to
|
||||
store floating-point values as byte strings. The Pack routines produce a bytes
|
||||
string from a C :c:type:`double`, and the Unpack routines produce a C
|
||||
:c:type:`double` from such a bytes string. The suffix (2, 4 or 8) specifies the
|
||||
number of bytes in the bytes string.
|
||||
|
||||
On platforms that appear to use IEEE 754 formats these functions work by
|
||||
copying bits. On other platforms, the 2-byte format is identical to the IEEE
|
||||
754 binary16 half-precision format, the 4-byte format (32-bit) is identical to
|
||||
the IEEE 754 binary32 single precision format, and the 8-byte format to the
|
||||
IEEE 754 binary64 double precision format, although the packing of INFs and
|
||||
NaNs (if such things exist on the platform) isn't handled correctly, and
|
||||
attempting to unpack a bytes string containing an IEEE INF or NaN will raise an
|
||||
exception.
|
||||
|
||||
On non-IEEE platforms with more precision, or larger dynamic range, than IEEE
|
||||
754 supports, not all values can be packed; on non-IEEE platforms with less
|
||||
precision, or smaller dynamic range, not all values can be unpacked. What
|
||||
happens in such cases is partly accidental (alas).
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
Pack functions
|
||||
--------------
|
||||
|
||||
The pack routines write 2, 4 or 8 bytes, starting at *p*. *le* is an
|
||||
:c:type:`int` argument, non-zero if you want the bytes string in little-endian
|
||||
format (exponent last, at ``p+1``, ``p+3``, or ``p+6`` ``p+7``), zero if you
|
||||
want big-endian format (exponent first, at *p*).
|
||||
|
||||
Return value: ``0`` if all is OK, ``-1`` if error (and an exception is set,
|
||||
most likely :exc:`OverflowError`).
|
||||
|
||||
There are two problems on non-IEEE platforms:
|
||||
|
||||
* What this does is undefined if *x* is a NaN or infinity.
|
||||
* ``-0.0`` and ``+0.0`` produce the same bytes string.
|
||||
|
||||
.. c:function:: int PyFloat_Pack2(double x, unsigned char *p, int le)
|
||||
|
||||
Pack a C double as the IEEE 754 binary16 half-precision format.
|
||||
|
||||
.. c:function:: int PyFloat_Pack4(double x, unsigned char *p, int le)
|
||||
|
||||
Pack a C double as the IEEE 754 binary32 single precision format.
|
||||
|
||||
.. c:function:: int PyFloat_Pack8(double x, unsigned char *p, int le)
|
||||
|
||||
Pack a C double as the IEEE 754 binary64 double precision format.
|
||||
|
||||
|
||||
Unpack functions
|
||||
----------------
|
||||
|
||||
The unpack routines read 2, 4 or 8 bytes, starting at *p*. *le* is an
|
||||
:c:type:`int` argument, non-zero if the bytes string is in little-endian format
|
||||
(exponent last, at ``p+1``, ``p+3`` or ``p+6`` and ``p+7``), zero if big-endian
|
||||
(exponent first, at *p*).
|
||||
|
||||
Return value: The unpacked double. On error, this is ``-1.0`` and
|
||||
:c:func:`PyErr_Occurred` is true (and an exception is set, most likely
|
||||
:exc:`OverflowError`).
|
||||
|
||||
Note that on a non-IEEE platform this will refuse to unpack a bytes string that
|
||||
represents a NaN or infinity.
|
||||
|
||||
.. c:function:: double PyFloat_Unpack2(const unsigned char *p, int le)
|
||||
|
||||
Unpack the IEEE 754 binary16 half-precision format as a C double.
|
||||
|
||||
.. c:function:: double PyFloat_Unpack4(const unsigned char *p, int le)
|
||||
|
||||
Unpack the IEEE 754 binary32 single precision format as a C double.
|
||||
|
||||
.. c:function:: double PyFloat_Unpack8(const unsigned char *p, int le)
|
||||
|
||||
Unpack the IEEE 754 binary64 double precision format as a C double.
|
||||
|
|
|
@ -766,6 +766,12 @@ New Features
|
|||
available directly (via :c:type:`PyCMethod`).
|
||||
(Contributed by Petr Viktorin in :issue:`46613`.)
|
||||
|
||||
* Add new functions to pack and unpack C double (serialize and deserialize):
|
||||
:c:func:`PyFloat_Pack2`, :c:func:`PyFloat_Pack4`, :c:func:`PyFloat_Pack8`,
|
||||
:c:func:`PyFloat_Unpack2`, :c:func:`PyFloat_Unpack4` and
|
||||
:c:func:`PyFloat_Unpack8`.
|
||||
(Contributed by Victor Stinner in :issue:`46906`.)
|
||||
|
||||
|
||||
Porting to Python 3.11
|
||||
----------------------
|
||||
|
|
|
@ -10,3 +10,12 @@ typedef struct {
|
|||
// Macro version of PyFloat_AsDouble() trading safety for speed.
|
||||
// It doesn't check if op is a double object.
|
||||
#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval)
|
||||
|
||||
|
||||
PyAPI_FUNC(int) PyFloat_Pack2(double x, char *p, int le);
|
||||
PyAPI_FUNC(int) PyFloat_Pack4(double x, char *p, int le);
|
||||
PyAPI_FUNC(int) PyFloat_Pack8(double x, char *p, int le);
|
||||
|
||||
PyAPI_FUNC(double) PyFloat_Unpack2(const char *p, int le);
|
||||
PyAPI_FUNC(double) PyFloat_Unpack4(const char *p, int le);
|
||||
PyAPI_FUNC(double) PyFloat_Unpack8(const char *p, int le);
|
||||
|
|
|
@ -38,54 +38,6 @@ struct _Py_float_state {
|
|||
#endif
|
||||
};
|
||||
|
||||
/* _PyFloat_{Pack,Unpack}{4,8}
|
||||
*
|
||||
* The struct and pickle (at least) modules need an efficient platform-
|
||||
* independent way to store floating-point values as byte strings.
|
||||
* The Pack routines produce a string from a C double, and the Unpack
|
||||
* routines produce a C double from such a string. The suffix (4 or 8)
|
||||
* specifies the number of bytes in the string.
|
||||
*
|
||||
* On platforms that appear to use (see _PyFloat_Init()) IEEE-754 formats
|
||||
* these functions work by copying bits. On other platforms, the formats the
|
||||
* 4- byte format is identical to the IEEE-754 single precision format, and
|
||||
* the 8-byte format to the IEEE-754 double precision format, although the
|
||||
* packing of INFs and NaNs (if such things exist on the platform) isn't
|
||||
* handled correctly, and attempting to unpack a string containing an IEEE
|
||||
* INF or NaN will raise an exception.
|
||||
*
|
||||
* On non-IEEE platforms with more precision, or larger dynamic range, than
|
||||
* 754 supports, not all values can be packed; on non-IEEE platforms with less
|
||||
* precision, or smaller dynamic range, not all values can be unpacked. What
|
||||
* happens in such cases is partly accidental (alas).
|
||||
*/
|
||||
|
||||
/* The pack routines write 2, 4 or 8 bytes, starting at p. le is a bool
|
||||
* argument, true if you want the string in little-endian format (exponent
|
||||
* last, at p+1, p+3 or p+7), false if you want big-endian format (exponent
|
||||
* first, at p).
|
||||
* Return value: 0 if all is OK, -1 if error (and an exception is
|
||||
* set, most likely OverflowError).
|
||||
* There are two problems on non-IEEE platforms:
|
||||
* 1): What this does is undefined if x is a NaN or infinity.
|
||||
* 2): -0.0 and +0.0 produce the same string.
|
||||
*/
|
||||
PyAPI_FUNC(int) _PyFloat_Pack2(double x, unsigned char *p, int le);
|
||||
PyAPI_FUNC(int) _PyFloat_Pack4(double x, unsigned char *p, int le);
|
||||
PyAPI_FUNC(int) _PyFloat_Pack8(double x, unsigned char *p, int le);
|
||||
|
||||
/* The unpack routines read 2, 4 or 8 bytes, starting at p. le is a bool
|
||||
* argument, true if the string is in little-endian format (exponent
|
||||
* last, at p+1, p+3 or p+7), false if big-endian (exponent first, at p).
|
||||
* Return value: The unpacked double. On error, this is -1.0 and
|
||||
* PyErr_Occurred() is true (and an exception is set, most likely
|
||||
* OverflowError). Note that on a non-IEEE platform this will refuse
|
||||
* to unpack a string that represents a NaN or infinity.
|
||||
*/
|
||||
PyAPI_FUNC(double) _PyFloat_Unpack2(const unsigned char *p, int le);
|
||||
PyAPI_FUNC(double) _PyFloat_Unpack4(const unsigned char *p, int le);
|
||||
PyAPI_FUNC(double) _PyFloat_Unpack8(const unsigned char *p, int le);
|
||||
|
||||
|
||||
PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out);
|
||||
|
||||
|
|
|
@ -12,7 +12,14 @@ from test.support import import_helper
|
|||
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
|
||||
INVALID_UNDERSCORE_LITERALS)
|
||||
from math import isinf, isnan, copysign, ldexp
|
||||
import math
|
||||
|
||||
try:
|
||||
import _testcapi
|
||||
except ImportError:
|
||||
_testcapi = None
|
||||
|
||||
HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
|
||||
INF = float("inf")
|
||||
NAN = float("nan")
|
||||
|
||||
|
@ -652,8 +659,9 @@ class IEEEFormatTestCase(unittest.TestCase):
|
|||
struct.unpack(fmt, data)
|
||||
|
||||
@support.requires_IEEE_754
|
||||
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
|
||||
def test_serialized_float_rounding(self):
|
||||
FLT_MAX = import_helper.import_module('_testcapi').FLT_MAX
|
||||
FLT_MAX = _testcapi.FLT_MAX
|
||||
self.assertEqual(struct.pack("<f", 3.40282356e38), struct.pack("<f", FLT_MAX))
|
||||
self.assertEqual(struct.pack("<f", -3.40282356e38), struct.pack("<f", -FLT_MAX))
|
||||
|
||||
|
@ -1488,5 +1496,69 @@ class HexFloatTestCase(unittest.TestCase):
|
|||
self.assertEqual(getattr(f, 'foo', 'none'), 'bar')
|
||||
|
||||
|
||||
# Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
|
||||
# Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
|
||||
BIG_ENDIAN = 0
|
||||
LITTLE_ENDIAN = 1
|
||||
EPSILON = {
|
||||
2: 2.0 ** -11, # binary16
|
||||
4: 2.0 ** -24, # binary32
|
||||
8: 2.0 ** -53, # binary64
|
||||
}
|
||||
|
||||
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
|
||||
class PackTests(unittest.TestCase):
|
||||
def test_pack(self):
|
||||
self.assertEqual(_testcapi.float_pack(2, 1.5, BIG_ENDIAN),
|
||||
b'>\x00')
|
||||
self.assertEqual(_testcapi.float_pack(4, 1.5, BIG_ENDIAN),
|
||||
b'?\xc0\x00\x00')
|
||||
self.assertEqual(_testcapi.float_pack(8, 1.5, BIG_ENDIAN),
|
||||
b'?\xf8\x00\x00\x00\x00\x00\x00')
|
||||
self.assertEqual(_testcapi.float_pack(2, 1.5, LITTLE_ENDIAN),
|
||||
b'\x00>')
|
||||
self.assertEqual(_testcapi.float_pack(4, 1.5, LITTLE_ENDIAN),
|
||||
b'\x00\x00\xc0?')
|
||||
self.assertEqual(_testcapi.float_pack(8, 1.5, LITTLE_ENDIAN),
|
||||
b'\x00\x00\x00\x00\x00\x00\xf8?')
|
||||
|
||||
def test_unpack(self):
|
||||
self.assertEqual(_testcapi.float_unpack(b'>\x00', BIG_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'?\xc0\x00\x00', BIG_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'\x00>', LITTLE_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN),
|
||||
1.5)
|
||||
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN),
|
||||
1.5)
|
||||
|
||||
def test_roundtrip(self):
|
||||
large = 2.0 ** 100
|
||||
values = [1.0, 1.5, large, 1.0/7, math.pi]
|
||||
if HAVE_IEEE_754:
|
||||
values.extend((INF, NAN))
|
||||
for value in values:
|
||||
for size in (2, 4, 8,):
|
||||
if size == 2 and value == large:
|
||||
# too large for 16-bit float
|
||||
continue
|
||||
rel_tol = EPSILON[size]
|
||||
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
|
||||
with self.subTest(value=value, size=size, endian=endian):
|
||||
data = _testcapi.float_pack(size, value, endian)
|
||||
value2 = _testcapi.float_unpack(data, endian)
|
||||
if isnan(value):
|
||||
self.assertTrue(isnan(value2), (value, value2))
|
||||
elif size < 8:
|
||||
self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol),
|
||||
(value, value2))
|
||||
else:
|
||||
self.assertEqual(value2, value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Add new functions to pack and unpack C double (serialize and deserialize):
|
||||
:c:func:`PyFloat_Pack2`, :c:func:`PyFloat_Pack4`, :c:func:`PyFloat_Pack8`,
|
||||
:c:func:`PyFloat_Unpack2`, :c:func:`PyFloat_Unpack4` and
|
||||
:c:func:`PyFloat_Unpack8`. Patch by Victor Stinner.
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include "pycore_bitutils.h" // _Py_bswap32()
|
||||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||
#include "pycore_floatobject.h" // _PyFloat_Pack8()
|
||||
|
||||
#include <ffi.h>
|
||||
#include "ctypes.h"
|
||||
|
@ -1009,10 +1008,10 @@ d_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
|
|||
if (x == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (_PyFloat_Pack8(x, (unsigned char *)ptr, 1))
|
||||
if (PyFloat_Pack8(x, ptr, 1))
|
||||
return NULL;
|
||||
#else
|
||||
if (_PyFloat_Pack8(x, (unsigned char *)ptr, 0))
|
||||
if (PyFloat_Pack8(x, ptr, 0))
|
||||
return NULL;
|
||||
#endif
|
||||
_RET(value);
|
||||
|
@ -1022,9 +1021,9 @@ static PyObject *
|
|||
d_get_sw(void *ptr, Py_ssize_t size)
|
||||
{
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
return PyFloat_FromDouble(_PyFloat_Unpack8(ptr, 1));
|
||||
return PyFloat_FromDouble(PyFloat_Unpack8(ptr, 1));
|
||||
#else
|
||||
return PyFloat_FromDouble(_PyFloat_Unpack8(ptr, 0));
|
||||
return PyFloat_FromDouble(PyFloat_Unpack8(ptr, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1057,10 +1056,10 @@ f_set_sw(void *ptr, PyObject *value, Py_ssize_t size)
|
|||
if (x == -1 && PyErr_Occurred())
|
||||
return NULL;
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
if (_PyFloat_Pack4(x, (unsigned char *)ptr, 1))
|
||||
if (PyFloat_Pack4(x, ptr, 1))
|
||||
return NULL;
|
||||
#else
|
||||
if (_PyFloat_Pack4(x, (unsigned char *)ptr, 0))
|
||||
if (PyFloat_Pack4(x, ptr, 0))
|
||||
return NULL;
|
||||
#endif
|
||||
_RET(value);
|
||||
|
@ -1070,9 +1069,9 @@ static PyObject *
|
|||
f_get_sw(void *ptr, Py_ssize_t size)
|
||||
{
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
return PyFloat_FromDouble(_PyFloat_Unpack4(ptr, 1));
|
||||
return PyFloat_FromDouble(PyFloat_Unpack4(ptr, 1));
|
||||
#else
|
||||
return PyFloat_FromDouble(_PyFloat_Unpack4(ptr, 0));
|
||||
return PyFloat_FromDouble(PyFloat_Unpack4(ptr, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#endif
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_floatobject.h" // _PyFloat_Pack8()
|
||||
#include "pycore_moduleobject.h" // _PyModule_GetState()
|
||||
#include "pycore_runtime.h" // _Py_ID()
|
||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||
|
@ -2244,7 +2243,7 @@ save_float(PicklerObject *self, PyObject *obj)
|
|||
if (self->bin) {
|
||||
char pdata[9];
|
||||
pdata[0] = BINFLOAT;
|
||||
if (_PyFloat_Pack8(x, (unsigned char *)&pdata[1], 0) < 0)
|
||||
if (PyFloat_Pack8(x, &pdata[1], 0) < 0)
|
||||
return -1;
|
||||
if (_Pickler_Write(self, pdata, 9) < 0)
|
||||
return -1;
|
||||
|
@ -5395,7 +5394,7 @@ load_binfloat(UnpicklerObject *self)
|
|||
if (_Unpickler_Read(self, &s, 8) < 0)
|
||||
return -1;
|
||||
|
||||
x = _PyFloat_Unpack8((unsigned char *)s, 0);
|
||||
x = PyFloat_Unpack8(s, 0);
|
||||
if (x == -1.0 && PyErr_Occurred())
|
||||
return -1;
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include "Python.h"
|
||||
#include "pycore_floatobject.h" // _PyFloat_Unpack2()
|
||||
#include "pycore_moduleobject.h" // _PyModule_GetState()
|
||||
#include "structmember.h" // PyMemberDef
|
||||
#include <ctype.h>
|
||||
|
@ -303,9 +302,7 @@ static PyObject *
|
|||
unpack_halffloat(const char *p, /* start of 2-byte string */
|
||||
int le) /* true for little-endian, false for big-endian */
|
||||
{
|
||||
double x;
|
||||
|
||||
x = _PyFloat_Unpack2((unsigned char *)p, le);
|
||||
double x = PyFloat_Unpack2(p, le);
|
||||
if (x == -1.0 && PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -324,7 +321,7 @@ pack_halffloat(_structmodulestate *state,
|
|||
"required argument is not a float");
|
||||
return -1;
|
||||
}
|
||||
return _PyFloat_Pack2(x, (unsigned char *)p, le);
|
||||
return PyFloat_Pack2(x, p, le);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
@ -333,7 +330,7 @@ unpack_float(const char *p, /* start of 4-byte string */
|
|||
{
|
||||
double x;
|
||||
|
||||
x = _PyFloat_Unpack4((unsigned char *)p, le);
|
||||
x = PyFloat_Unpack4(p, le);
|
||||
if (x == -1.0 && PyErr_Occurred())
|
||||
return NULL;
|
||||
return PyFloat_FromDouble(x);
|
||||
|
@ -345,7 +342,7 @@ unpack_double(const char *p, /* start of 8-byte string */
|
|||
{
|
||||
double x;
|
||||
|
||||
x = _PyFloat_Unpack8((unsigned char *)p, le);
|
||||
x = PyFloat_Unpack8(p, le);
|
||||
if (x == -1.0 && PyErr_Occurred())
|
||||
return NULL;
|
||||
return PyFloat_FromDouble(x);
|
||||
|
@ -979,7 +976,7 @@ bp_float(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
|
|||
"required argument is not a float");
|
||||
return -1;
|
||||
}
|
||||
return _PyFloat_Pack4(x, (unsigned char *)p, 0);
|
||||
return PyFloat_Pack4(x, p, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -991,7 +988,7 @@ bp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
|
|||
"required argument is not a float");
|
||||
return -1;
|
||||
}
|
||||
return _PyFloat_Pack8(x, (unsigned char *)p, 0);
|
||||
return PyFloat_Pack8(x, p, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1194,7 +1191,7 @@ lp_float(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
|
|||
"required argument is not a float");
|
||||
return -1;
|
||||
}
|
||||
return _PyFloat_Pack4(x, (unsigned char *)p, 1);
|
||||
return PyFloat_Pack4(x, p, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1206,7 +1203,7 @@ lp_double(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
|
|||
"required argument is not a float");
|
||||
return -1;
|
||||
}
|
||||
return _PyFloat_Pack8(x, (unsigned char *)p, 1);
|
||||
return PyFloat_Pack8(x, p, 1);
|
||||
}
|
||||
|
||||
static formatdef lilendian_table[] = {
|
||||
|
|
|
@ -5775,6 +5775,85 @@ test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
|
|||
}
|
||||
|
||||
|
||||
// Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
|
||||
static PyObject *
|
||||
test_float_pack(PyObject *self, PyObject *args)
|
||||
{
|
||||
int size;
|
||||
double d;
|
||||
int le;
|
||||
if (!PyArg_ParseTuple(args, "idi", &size, &d, &le)) {
|
||||
return NULL;
|
||||
}
|
||||
switch (size)
|
||||
{
|
||||
case 2:
|
||||
{
|
||||
char data[2];
|
||||
if (PyFloat_Pack2(d, data, le) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return PyBytes_FromStringAndSize(data, Py_ARRAY_LENGTH(data));
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
char data[4];
|
||||
if (PyFloat_Pack4(d, data, le) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return PyBytes_FromStringAndSize(data, Py_ARRAY_LENGTH(data));
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
char data[8];
|
||||
if (PyFloat_Pack8(d, data, le) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return PyBytes_FromStringAndSize(data, Py_ARRAY_LENGTH(data));
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_ValueError, "size must 2, 4 or 8");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
|
||||
static PyObject *
|
||||
test_float_unpack(PyObject *self, PyObject *args)
|
||||
{
|
||||
assert(!PyErr_Occurred());
|
||||
const char *data;
|
||||
Py_ssize_t size;
|
||||
int le;
|
||||
if (!PyArg_ParseTuple(args, "y#i", &data, &size, &le)) {
|
||||
return NULL;
|
||||
}
|
||||
double d;
|
||||
switch (size)
|
||||
{
|
||||
case 2:
|
||||
d = PyFloat_Unpack2(data, le);
|
||||
break;
|
||||
case 4:
|
||||
d = PyFloat_Unpack4(data, le);
|
||||
break;
|
||||
case 8:
|
||||
d = PyFloat_Unpack8(data, le);
|
||||
break;
|
||||
default:
|
||||
PyErr_SetString(PyExc_ValueError, "data length must 2, 4 or 8 bytes");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (d == -1.0 && PyErr_Occurred()) {
|
||||
return NULL;
|
||||
}
|
||||
return PyFloat_FromDouble(d);
|
||||
}
|
||||
|
||||
|
||||
static PyObject *negative_dictoffset(PyObject *, PyObject *);
|
||||
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
|
||||
static PyObject *getargs_s_hash_int(PyObject *, PyObject *, PyObject*);
|
||||
|
@ -6061,6 +6140,8 @@ static PyMethodDef TestMethods[] = {
|
|||
PyDoc_STR("fatal_error(message, release_gil=False): call Py_FatalError(message)")},
|
||||
{"type_get_version", type_get_version, METH_O, PyDoc_STR("type->tp_version_tag")},
|
||||
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
|
||||
{"float_pack", test_float_pack, METH_VARARGS, NULL},
|
||||
{"float_unpack", test_float_unpack, METH_VARARGS, NULL},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include "Python.h"
|
||||
#include "pycore_floatobject.h" // _PyFloat_Unpack4()
|
||||
#include "pycore_moduleobject.h" // _PyModule_GetState()
|
||||
#include "structmember.h" // PyMemberDef
|
||||
#include <stddef.h> // offsetof()
|
||||
|
@ -2056,15 +2055,14 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype,
|
|||
Py_ssize_t i;
|
||||
int le = (mformat_code == IEEE_754_FLOAT_LE) ? 1 : 0;
|
||||
Py_ssize_t itemcount = Py_SIZE(items) / 4;
|
||||
const unsigned char *memstr =
|
||||
(unsigned char *)PyBytes_AS_STRING(items);
|
||||
const char *memstr = PyBytes_AS_STRING(items);
|
||||
|
||||
converted_items = PyList_New(itemcount);
|
||||
if (converted_items == NULL)
|
||||
return NULL;
|
||||
for (i = 0; i < itemcount; i++) {
|
||||
PyObject *pyfloat = PyFloat_FromDouble(
|
||||
_PyFloat_Unpack4(&memstr[i * 4], le));
|
||||
PyFloat_Unpack4(&memstr[i * 4], le));
|
||||
if (pyfloat == NULL) {
|
||||
Py_DECREF(converted_items);
|
||||
return NULL;
|
||||
|
@ -2078,15 +2076,14 @@ array__array_reconstructor_impl(PyObject *module, PyTypeObject *arraytype,
|
|||
Py_ssize_t i;
|
||||
int le = (mformat_code == IEEE_754_DOUBLE_LE) ? 1 : 0;
|
||||
Py_ssize_t itemcount = Py_SIZE(items) / 8;
|
||||
const unsigned char *memstr =
|
||||
(unsigned char *)PyBytes_AS_STRING(items);
|
||||
const char *memstr = PyBytes_AS_STRING(items);
|
||||
|
||||
converted_items = PyList_New(itemcount);
|
||||
if (converted_items == NULL)
|
||||
return NULL;
|
||||
for (i = 0; i < itemcount; i++) {
|
||||
PyObject *pyfloat = PyFloat_FromDouble(
|
||||
_PyFloat_Unpack8(&memstr[i * 8], le));
|
||||
PyFloat_Unpack8(&memstr[i * 8], le));
|
||||
if (pyfloat == NULL) {
|
||||
Py_DECREF(converted_items);
|
||||
return NULL;
|
||||
|
|
|
@ -2033,7 +2033,7 @@ _PyFloat_DebugMallocStats(FILE *out)
|
|||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* _PyFloat_{Pack,Unpack}{2,4,8}. See floatobject.h.
|
||||
* PyFloat_{Pack,Unpack}{2,4,8}. See floatobject.h.
|
||||
* To match the NPY_HALF_ROUND_TIES_TO_EVEN behavior in:
|
||||
* https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c
|
||||
* We use:
|
||||
|
@ -2044,8 +2044,9 @@ _PyFloat_DebugMallocStats(FILE *out)
|
|||
*/
|
||||
|
||||
int
|
||||
_PyFloat_Pack2(double x, unsigned char *p, int le)
|
||||
PyFloat_Pack2(double x, char *data, int le)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)data;
|
||||
unsigned char sign;
|
||||
int e;
|
||||
double f;
|
||||
|
@ -2148,8 +2149,9 @@ _PyFloat_Pack2(double x, unsigned char *p, int le)
|
|||
}
|
||||
|
||||
int
|
||||
_PyFloat_Pack4(double x, unsigned char *p, int le)
|
||||
PyFloat_Pack4(double x, char *data, int le)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)data;
|
||||
if (float_format == unknown_format) {
|
||||
unsigned char sign;
|
||||
int e;
|
||||
|
@ -2255,8 +2257,9 @@ _PyFloat_Pack4(double x, unsigned char *p, int le)
|
|||
}
|
||||
|
||||
int
|
||||
_PyFloat_Pack8(double x, unsigned char *p, int le)
|
||||
PyFloat_Pack8(double x, char *data, int le)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)data;
|
||||
if (double_format == unknown_format) {
|
||||
unsigned char sign;
|
||||
int e;
|
||||
|
@ -2384,8 +2387,9 @@ _PyFloat_Pack8(double x, unsigned char *p, int le)
|
|||
}
|
||||
|
||||
double
|
||||
_PyFloat_Unpack2(const unsigned char *p, int le)
|
||||
PyFloat_Unpack2(const char *data, int le)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)data;
|
||||
unsigned char sign;
|
||||
int e;
|
||||
unsigned int f;
|
||||
|
@ -2446,8 +2450,9 @@ _PyFloat_Unpack2(const unsigned char *p, int le)
|
|||
}
|
||||
|
||||
double
|
||||
_PyFloat_Unpack4(const unsigned char *p, int le)
|
||||
PyFloat_Unpack4(const char *data, int le)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)data;
|
||||
if (float_format == unknown_format) {
|
||||
unsigned char sign;
|
||||
int e;
|
||||
|
@ -2524,8 +2529,9 @@ _PyFloat_Unpack4(const unsigned char *p, int le)
|
|||
}
|
||||
|
||||
double
|
||||
_PyFloat_Unpack8(const unsigned char *p, int le)
|
||||
PyFloat_Unpack8(const char *data, int le)
|
||||
{
|
||||
unsigned char *p = (unsigned char *)data;
|
||||
if (double_format == unknown_format) {
|
||||
unsigned char sign;
|
||||
int e;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "Python.h"
|
||||
#include "pycore_call.h" // _PyObject_CallNoArgs()
|
||||
#include "pycore_code.h" // _PyCode_New()
|
||||
#include "pycore_floatobject.h" // _PyFloat_Pack8()
|
||||
#include "pycore_hashtable.h" // _Py_hashtable_t
|
||||
#include "code.h"
|
||||
#include "marshal.h" // Py_MARSHAL_VERSION
|
||||
|
@ -271,8 +270,8 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p)
|
|||
static void
|
||||
w_float_bin(double v, WFILE *p)
|
||||
{
|
||||
unsigned char buf[8];
|
||||
if (_PyFloat_Pack8(v, buf, 1) < 0) {
|
||||
char buf[8];
|
||||
if (PyFloat_Pack8(v, buf, 1) < 0) {
|
||||
p->error = WFERR_UNMARSHALLABLE;
|
||||
return;
|
||||
}
|
||||
|
@ -883,10 +882,10 @@ r_PyLong(RFILE *p)
|
|||
static double
|
||||
r_float_bin(RFILE *p)
|
||||
{
|
||||
const unsigned char *buf = (const unsigned char *) r_string(8, p);
|
||||
const char *buf = r_string(8, p);
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
return _PyFloat_Unpack8(buf, 1);
|
||||
return PyFloat_Unpack8(buf, 1);
|
||||
}
|
||||
|
||||
/* Issue #33720: Disable inlining for reducing the C stack consumption
|
||||
|
|
Loading…
Reference in New Issue