Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Lib/test/test_capi/test_float.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@ def test_pack_unpack_roundtrip_for_nans(self):
signaling = 0
quiet = int(not signaling)
if size == 8:
payload = random.randint(signaling, 1 << 50)
payload = random.randint(signaling, 0x7ffffffffffff)
i = (sign << 63) + (0x7ff << 52) + (quiet << 51) + payload
elif size == 4:
payload = random.randint(signaling, 1 << 21)
payload = random.randint(signaling, 0x3fffff)
i = (sign << 31) + (0xff << 23) + (quiet << 22) + payload
elif size == 2:
payload = random.randint(signaling, 1 << 8)
payload = random.randint(signaling, 0x1ff)
i = (sign << 15) + (0x1f << 10) + (quiet << 9) + payload
data = bytes.fromhex(f'{i:x}')
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Workaround NaN's "canonicalization" in :c:func:`PyFloat_Pack4`
and :c:func:`PyFloat_Unpack4` on RISC-V.
39 changes: 31 additions & 8 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2194,14 +2194,27 @@ PyFloat_Pack4(double x, char *data, int le)
/* correct y if x was a sNaN, transformed to qNaN by conversion */
if (isnan(x)) {
uint64_t v;
uint32_t u32;

memcpy(&v, &x, 8);
memcpy(&u32, &y, 4);

if ((v & (1ULL << 51)) == 0) {
uint32_t u32;
memcpy(&u32, &y, 4);
u32 &= ~(1 << 22); /* make sNaN */
memcpy(&y, &u32, 4);
}

/* Workaround RISC-V: "If a NaN value is converted to a
* different floating-point type, the result is the
* canonical NaN of the new type". The canonical NaN here
* is a positive qNaN with zero payload. */
if (v & (1ULL << 63)) {
u32 |= (1 << 31); /* set sign */
}
/* add payload */
u32 -= (u32 & 0x3fffff);
u32 += (uint32_t)((v & 0x7ffffffffffffULL) >> 29);

memcpy(&y, &u32, 4);
}

unsigned char s[sizeof(float)];
Expand Down Expand Up @@ -2490,17 +2503,27 @@ PyFloat_Unpack4(const char *data, int le)

/* return sNaN double if x was sNaN float */
if (isnan(x)) {
double y = x; /* will make qNaN double */
uint32_t v;
uint64_t u64;

memcpy(&v, &x, 4);
memcpy(&u64, &y, 8);

if ((v & (1 << 22)) == 0) {
double y = x; /* will make qNaN double */
uint64_t u64;
memcpy(&u64, &y, 8);
u64 &= ~(1ULL << 51); /* make sNaN */
memcpy(&y, &u64, 8);
return y;
}

/* Workaround RISC-V, see PyFloat_Pack4() */
if (v & (1 << 31)) {
u64 |= (1ULL << 63); /* set sign */
}
/* add payload */
u64 -= (u64 & 0x7ffffffffffffULL);
u64 += ((v & 0x3fffffULL) << 29);

memcpy(&y, &u64, 8);
return y;
}

return x;
Expand Down
Loading