Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ad2f088

Browse filesBrowse files
authored
gh-130317: Fix test_pack_unpack_roundtrip() and add docs (#133204)
* Skip sNaN's testing in 32-bit mode. * Drop float_set_snan() helper. * Use memcpy() workaround for sNaN's in PyFloat_Unpack4(). * Document, that sNaN's may not be preserved by PyFloat_Pack/Unpack API.
1 parent ed039b8 commit ad2f088
Copy full SHA for ad2f088

File tree

Expand file treeCollapse file tree

5 files changed

+18
-62
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+18
-62
lines changed

‎Doc/c-api/float.rst

Copy file name to clipboardExpand all lines: Doc/c-api/float.rst
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ NaNs (if such things exist on the platform) isn't handled correctly, and
9696
attempting to unpack a bytes string containing an IEEE INF or NaN will raise an
9797
exception.
9898
99+
Note that NaNs type may not be preserved on IEEE platforms (silent NaN become
100+
quiet), for example on x86 systems in 32-bit mode.
101+
99102
On non-IEEE platforms with more precision, or larger dynamic range, than IEEE
100103
754 supports, not all values can be packed; on non-IEEE platforms with less
101104
precision, or smaller dynamic range, not all values can be unpacked. What

‎Lib/test/test_capi/test_float.py

Copy file name to clipboardExpand all lines: Lib/test/test_capi/test_float.py
+10-13Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,20 +180,23 @@ def test_pack_unpack_roundtrip(self):
180180
self.assertEqual(value2, value)
181181

182182
@unittest.skipUnless(HAVE_IEEE_754, "requires IEEE 754")
183-
# Skip on x86 (32-bit), since these tests fail. The problem is that sNaN
184-
# doubles become qNaN doubles just by the C calling convention, there is no
185-
# way to preserve sNaN doubles between C function calls. But tests pass
186-
# on Windows x86.
187-
@unittest.skipIf((sys.maxsize == 2147483647) and not(sys.platform == 'win32'),
188-
'test fails on x86 (32-bit)')
189183
def test_pack_unpack_roundtrip_for_nans(self):
190184
pack = _testcapi.float_pack
191185
unpack = _testcapi.float_unpack
192186

193187
for _ in range(10):
194188
for size in (2, 4, 8):
195189
sign = random.randint(0, 1)
196-
signaling = random.randint(0, 1)
190+
if sys.maxsize != 2147483647: # not it 32-bit mode
191+
signaling = random.randint(0, 1)
192+
else:
193+
# Skip sNaN's on x86 (32-bit). The problem is that sNaN
194+
# doubles become qNaN doubles just by the C calling
195+
# convention, there is no way to preserve sNaN doubles
196+
# between C function calls with the current
197+
# PyFloat_Pack/Unpack*() API. See also gh-130317 and
198+
# e.g. https://developercommunity.visualstudio.com/t/155064
199+
signaling = 0
197200
quiet = int(not signaling)
198201
if size == 8:
199202
payload = random.randint(signaling, 1 << 50)
@@ -209,12 +212,6 @@ def test_pack_unpack_roundtrip_for_nans(self):
209212
with self.subTest(data=data, size=size, endian=endian):
210213
data1 = data if endian == BIG_ENDIAN else data[::-1]
211214
value = unpack(data1, endian)
212-
if signaling and sys.platform == 'win32':
213-
# On Windows x86, sNaN becomes qNaN when returned
214-
# from function. That's a known bug, e.g.
215-
# https://developercommunity.visualstudio.com/t/155064
216-
# (see also gh-130317).
217-
value = _testcapi.float_set_snan(value)
218215
data2 = pack(size, value, endian)
219216
self.assertTrue(math.isnan(value))
220217
self.assertEqual(data1, data2)

‎Modules/_testcapi/clinic/float.c.h

Copy file name to clipboardExpand all lines: Modules/_testcapi/clinic/float.c.h
+1-10Lines changed: 1 addition & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/_testcapi/float.c

Copy file name to clipboardExpand all lines: Modules/_testcapi/float.c
-33Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -157,42 +157,9 @@ test_string_to_double(PyObject *self, PyObject *Py_UNUSED(ignored))
157157
}
158158

159159

160-
/*[clinic input]
161-
_testcapi.float_set_snan
162-
163-
obj: object
164-
/
165-
166-
Make a signaling NaN.
167-
[clinic start generated code]*/
168-
169-
static PyObject *
170-
_testcapi_float_set_snan(PyObject *module, PyObject *obj)
171-
/*[clinic end generated code: output=f43778a70f60aa4b input=c1269b0f88ef27ac]*/
172-
{
173-
if (!PyFloat_Check(obj)) {
174-
PyErr_SetString(PyExc_ValueError, "float-point number expected");
175-
return NULL;
176-
}
177-
double d = ((PyFloatObject *)obj)->ob_fval;
178-
if (!isnan(d)) {
179-
PyErr_SetString(PyExc_ValueError, "nan expected");
180-
return NULL;
181-
}
182-
uint64_t v;
183-
memcpy(&v, &d, 8);
184-
v &= ~(1ULL << 51); /* make sNaN */
185-
186-
// gh-130317: memcpy() is needed to preserve the sNaN flag on x86 (32-bit)
187-
PyObject *res = PyFloat_FromDouble(0.0);
188-
memcpy(&((PyFloatObject *)res)->ob_fval, &v, 8);
189-
return res;
190-
}
191-
192160
static PyMethodDef test_methods[] = {
193161
_TESTCAPI_FLOAT_PACK_METHODDEF
194162
_TESTCAPI_FLOAT_UNPACK_METHODDEF
195-
_TESTCAPI_FLOAT_SET_SNAN_METHODDEF
196163
{"test_string_to_double", test_string_to_double, METH_NOARGS},
197164
{NULL},
198165
};

‎Objects/floatobject.c

Copy file name to clipboardExpand all lines: Objects/floatobject.c
+4-6Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2495,12 +2495,10 @@ PyFloat_Unpack4(const char *data, int le)
24952495

24962496
if ((v & (1 << 22)) == 0) {
24972497
double y = x; /* will make qNaN double */
2498-
union double_val {
2499-
double d;
2500-
uint64_t u64;
2501-
} *py = (union double_val *)&y;
2502-
2503-
py->u64 &= ~(1ULL << 51); /* make sNaN */
2498+
uint64_t u64;
2499+
memcpy(&u64, &y, 8);
2500+
u64 &= ~(1ULL << 51); /* make sNaN */
2501+
memcpy(&y, &u64, 8);
25042502
return y;
25052503
}
25062504
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.