2929NAN = float ("nan" )
3030
3131
32+ def make_nan (size , sign , quiet , payload = None ):
33+ if size == 8 :
34+ payload_mask = 0x7ffffffffffff
35+ i = (sign << 63 ) + (0x7ff << 52 ) + (quiet << 51 )
36+ elif size == 4 :
37+ payload_mask = 0x3fffff
38+ i = (sign << 31 ) + (0xff << 23 ) + (quiet << 22 )
39+ elif size == 2 :
40+ payload_mask = 0x1ff
41+ i = (sign << 15 ) + (0x1f << 10 ) + (quiet << 9 )
42+ else :
43+ raise ValueError ("size must be either 2, 4, or 8" )
44+ if payload is None :
45+ payload = random .randint (not quiet , payload_mask )
46+ return i + payload
47+
48+
3249class CAPIFloatTest (unittest .TestCase ):
3350 def test_check (self ):
3451 # Test PyFloat_Check()
@@ -202,16 +219,7 @@ def test_pack_unpack_roundtrip_for_nans(self):
202219 # HP PA RISC uses 0 for quiet, see:
203220 # https://en.wikipedia.org/wiki/NaN#Encoding
204221 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
222+ i = make_nan (size , sign , not signaling )
215223 data = bytes .fromhex (f'{ i :x} ' )
216224 for endian in (BIG_ENDIAN , LITTLE_ENDIAN ):
217225 with self .subTest (data = data , size = size , endian = endian ):
@@ -221,6 +229,32 @@ def test_pack_unpack_roundtrip_for_nans(self):
221229 self .assertTrue (math .isnan (value ))
222230 self .assertEqual (data1 , data2 )
223231
232+ @unittest .skipUnless (HAVE_IEEE_754 , "requires IEEE 754" )
233+ @unittest .skipUnless (sys .maxsize != 2147483647 , "requires 64-bit mode" )
234+ def test_pack_unpack_nans_for_different_formats (self ):
235+ pack = _testcapi .float_pack
236+ unpack = _testcapi .float_unpack
237+
238+ for endian in (BIG_ENDIAN , LITTLE_ENDIAN ):
239+ with self .subTest (endian = endian ):
240+ byteorder = "big" if endian == BIG_ENDIAN else "little"
241+
242+ # Convert sNaN to qNaN, if payload got truncated
243+ data = make_nan (8 , 0 , False , 0x80001 ).to_bytes (8 , byteorder )
244+ snan_low = unpack (data , endian )
245+ qnan4 = make_nan (4 , 0 , True , 0 ).to_bytes (4 , byteorder )
246+ qnan2 = make_nan (2 , 0 , True , 0 ).to_bytes (2 , byteorder )
247+ self .assertEqual (pack (4 , snan_low , endian ), qnan4 )
248+ self .assertEqual (pack (2 , snan_low , endian ), qnan2 )
249+
250+ # Preserve NaN type, if payload not truncated
251+ data = make_nan (8 , 0 , False , 0x80000000001 ).to_bytes (8 , byteorder )
252+ snan_high = unpack (data , endian )
253+ snan4 = make_nan (4 , 0 , False , 16384 ).to_bytes (4 , byteorder )
254+ snan2 = make_nan (2 , 0 , False , 2 ).to_bytes (2 , byteorder )
255+ self .assertEqual (pack (4 , snan_high , endian ), snan4 )
256+ self .assertEqual (pack (2 , snan_high , endian ), snan2 )
257+
224258
225259if __name__ == "__main__" :
226260 unittest .main ()
0 commit comments