2929NAN = float ("nan" )
3030
3131
32+ def make_nan (size , sign , quiet , payload ):
33+ if size == 8 :
34+ payload &= 0x7ffffffffffff
35+ i = (sign << 63 ) + (0x7ff << 52 ) + (quiet << 51 ) + payload
36+ elif size == 4 :
37+ payload &= 0x3fffff
38+ i = (sign << 31 ) + (0xff << 23 ) + (quiet << 22 ) + payload
39+ elif size == 2 :
40+ payload &= 0x1ff
41+ i = (sign << 15 ) + (0x1f << 10 ) + (quiet << 9 ) + payload
42+ else :
43+ raise ValueError ("size must be either 2, 4, or 8" )
44+ return i
45+
46+
3247class CAPIFloatTest (unittest .TestCase ):
3348 def test_check (self ):
3449 # Test PyFloat_Check()
@@ -202,16 +217,8 @@ def test_pack_unpack_roundtrip_for_nans(self):
202217 # HP PA RISC uses 0 for quiet, see:
203218 # https://en.wikipedia.org/wiki/NaN#Encoding
204219 signaling = 1
205- quiet = int (not signaling )
206- if size == 8 :
207- payload = random .randint (signaling , 0x7ffffffffffff )
208- i = (sign << 63 ) + (0x7ff << 52 ) + (quiet << 51 ) + payload
209- elif size == 4 :
210- payload = random .randint (signaling , 0x3fffff )
211- i = (sign << 31 ) + (0xff << 23 ) + (quiet << 22 ) + payload
212- elif size == 2 :
213- payload = random .randint (signaling , 0x1ff )
214- i = (sign << 15 ) + (0x1f << 10 ) + (quiet << 9 ) + payload
220+ payload = random .randint (signaling , 0xfffffffffffff )
221+ i = make_nan (size , sign , not signaling , payload )
215222 data = bytes .fromhex (f'{ i :x} ' )
216223 for endian in (BIG_ENDIAN , LITTLE_ENDIAN ):
217224 with self .subTest (data = data , size = size , endian = endian ):
@@ -221,6 +228,31 @@ def test_pack_unpack_roundtrip_for_nans(self):
221228 self .assertTrue (math .isnan (value ))
222229 self .assertEqual (data1 , data2 )
223230
231+ @unittest .skipUnless (HAVE_IEEE_754 , "requires IEEE 754" )
232+ def test_pack_unpack_nans_for_different_formats (self ):
233+ pack = _testcapi .float_pack
234+ unpack = _testcapi .float_unpack
235+
236+ for endian in (BIG_ENDIAN , LITTLE_ENDIAN ):
237+ with self .subTest (endian = endian ):
238+ byteorder = "big" if endian == BIG_ENDIAN else "little"
239+
240+ # Convert sNaN to qNaN, if payload got truncated
241+ data = make_nan (8 , 0 , False , 0x80001 ).to_bytes (8 , byteorder )
242+ snan_low = unpack (data , endian )
243+ qnan4 = make_nan (4 , 0 , True , 0 ).to_bytes (4 , byteorder )
244+ qnan2 = make_nan (2 , 0 , True , 0 ).to_bytes (2 , byteorder )
245+ self .assertEqual (pack (4 , snan_low , endian ), qnan4 )
246+ self .assertEqual (pack (2 , snan_low , endian ), qnan2 )
247+
248+ # Preserve NaN type, if payload not truncated
249+ data = make_nan (8 , 0 , False , 0x80000000001 ).to_bytes (8 , byteorder )
250+ snan_high = unpack (data , endian )
251+ snan4 = make_nan (4 , 0 , False , 16384 ).to_bytes (4 , byteorder )
252+ snan2 = make_nan (2 , 0 , False , 2 ).to_bytes (2 , byteorder )
253+ self .assertEqual (pack (4 , snan_high , endian ), snan4 )
254+ self .assertEqual (pack (2 , snan_high , endian ), snan2 )
255+
224256
225257if __name__ == "__main__" :
226258 unittest .main ()
0 commit comments