diff options
author | Sergey B Kirpichev <skirpichev@gmail.com> | 2025-04-28 13:23:26 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-28 13:23:26 (GMT) |
commit | 6157135a8d0bc2dbd5c24d1648d04a9c24a7d17e (patch) | |
tree | 0edd1de1b97b5f7178d8ea9daceb888a0e3ee56e /Objects/floatobject.c | |
parent | 922049b613d155ade4c4a8f83452767bea003a9f (diff) | |
download | cpython-6157135a8d0bc2dbd5c24d1648d04a9c24a7d17e.zip cpython-6157135a8d0bc2dbd5c24d1648d04a9c24a7d17e.tar.gz cpython-6157135a8d0bc2dbd5c24d1648d04a9c24a7d17e.tar.bz2 |
gh-130317: Fix PyFloat_Pack/Unpack[24] for NaN's with payload (#130452)
Co-authored-by: Victor Stinner <vstinner@python.org>
Diffstat (limited to 'Objects/floatobject.c')
-rw-r--r-- | Objects/floatobject.c | 49 |
1 files changed, 42 insertions, 7 deletions
diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 87a00bf..e0a8f0c 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2021,14 +2021,13 @@ PyFloat_Pack2(double x, char *data, int le) bits = 0; } else if (isnan(x)) { - /* There are 2046 distinct half-precision NaNs (1022 signaling and - 1024 quiet), but there are only two quiet NaNs that don't arise by - quieting a signaling NaN; we get those by setting the topmost bit - of the fraction field and clearing all other fraction bits. We - choose the one with the appropriate sign. */ sign = (copysign(1.0, x) == -1.0); e = 0x1f; - bits = 512; + + uint64_t v; + memcpy(&v, &x, sizeof(v)); + v &= 0xffc0000000000ULL; + bits = (unsigned short)(v >> 42); /* NaN's type & payload */ } else { sign = (x < 0.0); @@ -2192,6 +2191,21 @@ PyFloat_Pack4(double x, char *data, int le) if (isinf(y) && !isinf(x)) goto Overflow; + /* correct y if x was a sNaN, transformed to qNaN by conversion */ + if (isnan(x)) { + uint64_t v; + + memcpy(&v, &x, 8); + if ((v & (1ULL << 51)) == 0) { + union float_val { + float f; + uint32_t u32; + } *py = (union float_val *)&y; + + py->u32 &= ~(1 << 22); /* make sNaN */ + } + } + unsigned char s[sizeof(float)]; memcpy(s, &y, sizeof(float)); @@ -2374,7 +2388,11 @@ PyFloat_Unpack2(const char *data, int le) } else { /* NaN */ - return sign ? -fabs(Py_NAN) : fabs(Py_NAN); + uint64_t v = sign ? 0xfff0000000000000ULL : 0x7ff0000000000000ULL; + + v += (uint64_t)f << 42; /* add NaN's type & payload */ + memcpy(&x, &v, sizeof(v)); + return x; } } @@ -2470,6 +2488,23 @@ PyFloat_Unpack4(const char *data, int le) memcpy(&x, p, 4); } + /* return sNaN double if x was sNaN float */ + if (isnan(x)) { + uint32_t v; + memcpy(&v, &x, 4); + + if ((v & (1 << 22)) == 0) { + double y = x; /* will make qNaN double */ + union double_val { + double d; + uint64_t u64; + } *py = (union double_val *)&y; + + py->u64 &= ~(1ULL << 51); /* make sNaN */ + return y; + } + } + return x; } } |