88import unittest
99
1010from test import support
11+ from test .support import import_helper
1112from test .test_grammar import (VALID_UNDERSCORE_LITERALS ,
1213 INVALID_UNDERSCORE_LITERALS )
1314from math import isinf , isnan , copysign , ldexp
15+ import math
1416
17+ try :
18+ import _testcapi
19+ except ImportError :
20+ _testcapi = None
21+
22+ HAVE_IEEE_754 = float .__getformat__ ("double" ).startswith ("IEEE" )
1523INF = float ("inf" )
1624NAN = float ("nan" )
1725
18- have_getformat = hasattr (float , "__getformat__" )
19- requires_getformat = unittest .skipUnless (have_getformat ,
20- "requires __getformat__" )
21- requires_setformat = unittest .skipUnless (hasattr (float , "__setformat__" ),
22- "requires __setformat__" )
2326
2427#locate file with float format test values
2528test_dir = os .path .dirname (__file__ ) or os .curdir
@@ -139,6 +142,10 @@ def check(s):
139142 check ('123\xbd ' )
140143 check (' 123 456 ' )
141144 check (b' 123 456 ' )
145+ # all whitespace (cf. https://github.com/python/cpython/issues/95605)
146+ check ('' )
147+ check (' ' )
148+ check ('\t \n ' )
142149
143150 # non-ascii digits (error came from non-digit '!')
144151 check ('\u0663 \u0661 \u0664 !' )
@@ -249,6 +256,35 @@ def test_keyword_args(self):
249256 with self .assertRaisesRegex (TypeError , 'keyword argument' ):
250257 float (x = '3.14' )
251258
259+ # TODO: RUSTPYTHON
260+ @unittest .expectedFailure
261+ def test_keywords_in_subclass (self ):
262+ class subclass (float ):
263+ pass
264+ u = subclass (2.5 )
265+ self .assertIs (type (u ), subclass )
266+ self .assertEqual (float (u ), 2.5 )
267+ with self .assertRaises (TypeError ):
268+ subclass (x = 0 )
269+
270+ class subclass_with_init (float ):
271+ def __init__ (self , arg , newarg = None ):
272+ self .newarg = newarg
273+ u = subclass_with_init (2.5 , newarg = 3 )
274+ self .assertIs (type (u ), subclass_with_init )
275+ self .assertEqual (float (u ), 2.5 )
276+ self .assertEqual (u .newarg , 3 )
277+
278+ class subclass_with_new (float ):
279+ def __new__ (cls , arg , newarg = None ):
280+ self = super ().__new__ (cls , arg )
281+ self .newarg = newarg
282+ return self
283+ u = subclass_with_new (2.5 , newarg = 3 )
284+ self .assertIs (type (u ), subclass_with_new )
285+ self .assertEqual (float (u ), 2.5 )
286+ self .assertEqual (u .newarg , 3 )
287+
252288 def test_is_integer (self ):
253289 self .assertFalse ((1.1 ).is_integer ())
254290 self .assertTrue ((1. ).is_integer ())
@@ -590,17 +626,8 @@ class F(float, H):
590626 self .assertEqual (hash (value ), object .__hash__ (value ))
591627
592628
593- @requires_setformat
629+ @unittest . skipUnless ( hasattr ( float , "__getformat__" ), "requires __getformat__" )
594630class FormatFunctionsTestCase (unittest .TestCase ):
595-
596- def setUp (self ):
597- self .save_formats = {'double' :float .__getformat__ ('double' ),
598- 'float' :float .__getformat__ ('float' )}
599-
600- def tearDown (self ):
601- float .__setformat__ ('double' , self .save_formats ['double' ])
602- float .__setformat__ ('float' , self .save_formats ['float' ])
603-
604631 def test_getformat (self ):
605632 self .assertIn (float .__getformat__ ('double' ),
606633 ['unknown' , 'IEEE, big-endian' , 'IEEE, little-endian' ])
@@ -609,24 +636,6 @@ def test_getformat(self):
609636 self .assertRaises (ValueError , float .__getformat__ , 'chicken' )
610637 self .assertRaises (TypeError , float .__getformat__ , 1 )
611638
612- def test_setformat (self ):
613- for t in 'double' , 'float' :
614- float .__setformat__ (t , 'unknown' )
615- if self .save_formats [t ] == 'IEEE, big-endian' :
616- self .assertRaises (ValueError , float .__setformat__ ,
617- t , 'IEEE, little-endian' )
618- elif self .save_formats [t ] == 'IEEE, little-endian' :
619- self .assertRaises (ValueError , float .__setformat__ ,
620- t , 'IEEE, big-endian' )
621- else :
622- self .assertRaises (ValueError , float .__setformat__ ,
623- t , 'IEEE, big-endian' )
624- self .assertRaises (ValueError , float .__setformat__ ,
625- t , 'IEEE, little-endian' )
626- self .assertRaises (ValueError , float .__setformat__ ,
627- t , 'chicken' )
628- self .assertRaises (ValueError , float .__setformat__ ,
629- 'chicken' , 'unknown' )
630639
631640BE_DOUBLE_INF = b'\x7f \xf0 \x00 \x00 \x00 \x00 \x00 \x00 '
632641LE_DOUBLE_INF = bytes (reversed (BE_DOUBLE_INF ))
@@ -638,36 +647,6 @@ def test_setformat(self):
638647BE_FLOAT_NAN = b'\x7f \xc0 \x00 \x00 '
639648LE_FLOAT_NAN = bytes (reversed (BE_FLOAT_NAN ))
640649
641- # on non-IEEE platforms, attempting to unpack a bit pattern
642- # representing an infinity or a NaN should raise an exception.
643-
644- @requires_setformat
645- class UnknownFormatTestCase (unittest .TestCase ):
646- def setUp (self ):
647- self .save_formats = {'double' :float .__getformat__ ('double' ),
648- 'float' :float .__getformat__ ('float' )}
649- float .__setformat__ ('double' , 'unknown' )
650- float .__setformat__ ('float' , 'unknown' )
651-
652- def tearDown (self ):
653- float .__setformat__ ('double' , self .save_formats ['double' ])
654- float .__setformat__ ('float' , self .save_formats ['float' ])
655-
656- def test_double_specials_dont_unpack (self ):
657- for fmt , data in [('>d' , BE_DOUBLE_INF ),
658- ('>d' , BE_DOUBLE_NAN ),
659- ('<d' , LE_DOUBLE_INF ),
660- ('<d' , LE_DOUBLE_NAN )]:
661- self .assertRaises (ValueError , struct .unpack , fmt , data )
662-
663- def test_float_specials_dont_unpack (self ):
664- for fmt , data in [('>f' , BE_FLOAT_INF ),
665- ('>f' , BE_FLOAT_NAN ),
666- ('<f' , LE_FLOAT_INF ),
667- ('<f' , LE_FLOAT_NAN )]:
668- self .assertRaises (ValueError , struct .unpack , fmt , data )
669-
670-
671650# on an IEEE platform, all we guarantee is that bit patterns
672651# representing infinities or NaNs do not raise an exception; all else
673652# is accident (today).
@@ -694,8 +673,9 @@ def test_float_specials_do_unpack(self):
694673 # TODO: RUSTPYTHON
695674 @unittest .expectedFailure
696675 @support .requires_IEEE_754
676+ @unittest .skipIf (_testcapi is None , 'needs _testcapi' )
697677 def test_serialized_float_rounding (self ):
698- from _testcapi import FLT_MAX
678+ FLT_MAX = _testcapi . FLT_MAX
699679 self .assertEqual (struct .pack ("<f" , 3.40282356e38 ), struct .pack ("<f" , FLT_MAX ))
700680 self .assertEqual (struct .pack ("<f" , - 3.40282356e38 ), struct .pack ("<f" , - FLT_MAX ))
701681
@@ -735,18 +715,16 @@ def test_format(self):
735715 # conversion to string should fail
736716 self .assertRaises (ValueError , format , 3.0 , "s" )
737717
738- # other format specifiers shouldn't work on floats,
739- # in particular int specifiers
740- for format_spec in ([chr (x ) for x in range (ord ('a' ), ord ('z' )+ 1 )] +
741- [chr (x ) for x in range (ord ('A' ), ord ('Z' )+ 1 )]):
742- if not format_spec in 'eEfFgGn%' :
743- self .assertRaises (ValueError , format , 0.0 , format_spec )
744- self .assertRaises (ValueError , format , 1.0 , format_spec )
745- self .assertRaises (ValueError , format , - 1.0 , format_spec )
746- self .assertRaises (ValueError , format , 1e100 , format_spec )
747- self .assertRaises (ValueError , format , - 1e100 , format_spec )
748- self .assertRaises (ValueError , format , 1e-100 , format_spec )
749- self .assertRaises (ValueError , format , - 1e-100 , format_spec )
718+ # confirm format options expected to fail on floats, such as integer
719+ # presentation types
720+ for format_spec in 'sbcdoxX' :
721+ self .assertRaises (ValueError , format , 0.0 , format_spec )
722+ self .assertRaises (ValueError , format , 1.0 , format_spec )
723+ self .assertRaises (ValueError , format , - 1.0 , format_spec )
724+ self .assertRaises (ValueError , format , 1e100 , format_spec )
725+ self .assertRaises (ValueError , format , - 1e100 , format_spec )
726+ self .assertRaises (ValueError , format , 1e-100 , format_spec )
727+ self .assertRaises (ValueError , format , - 1e-100 , format_spec )
750728
751729 # issue 3382
752730 self .assertEqual (format (NAN , 'f' ), 'nan' )
@@ -868,6 +846,11 @@ def test_inf_nan(self):
868846 self .assertRaises (TypeError , round , NAN , "ceci n'est pas un integer" )
869847 self .assertRaises (TypeError , round , - 0.0 , 1j )
870848
849+ def test_inf_nan_ndigits (self ):
850+ self .assertEqual (round (INF , 0 ), INF )
851+ self .assertEqual (round (- INF , 0 ), - INF )
852+ self .assertTrue (math .isnan (round (NAN , 0 )))
853+
871854 def test_large_n (self ):
872855 for n in [324 , 325 , 400 , 2 ** 31 - 1 , 2 ** 31 , 2 ** 32 , 2 ** 100 ]:
873856 self .assertEqual (round (123.456 , n ), 123.456 )
@@ -1538,5 +1521,69 @@ def __init__(self, value):
15381521 self .assertEqual (getattr (f , 'foo' , 'none' ), 'bar' )
15391522
15401523
1524+ # Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
1525+ # Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
1526+ BIG_ENDIAN = 0
1527+ LITTLE_ENDIAN = 1
1528+ EPSILON = {
1529+ 2 : 2.0 ** - 11 , # binary16
1530+ 4 : 2.0 ** - 24 , # binary32
1531+ 8 : 2.0 ** - 53 , # binary64
1532+ }
1533+
1534+ @unittest .skipIf (_testcapi is None , 'needs _testcapi' )
1535+ class PackTests (unittest .TestCase ):
1536+ def test_pack (self ):
1537+ self .assertEqual (_testcapi .float_pack (2 , 1.5 , BIG_ENDIAN ),
1538+ b'>\x00 ' )
1539+ self .assertEqual (_testcapi .float_pack (4 , 1.5 , BIG_ENDIAN ),
1540+ b'?\xc0 \x00 \x00 ' )
1541+ self .assertEqual (_testcapi .float_pack (8 , 1.5 , BIG_ENDIAN ),
1542+ b'?\xf8 \x00 \x00 \x00 \x00 \x00 \x00 ' )
1543+ self .assertEqual (_testcapi .float_pack (2 , 1.5 , LITTLE_ENDIAN ),
1544+ b'\x00 >' )
1545+ self .assertEqual (_testcapi .float_pack (4 , 1.5 , LITTLE_ENDIAN ),
1546+ b'\x00 \x00 \xc0 ?' )
1547+ self .assertEqual (_testcapi .float_pack (8 , 1.5 , LITTLE_ENDIAN ),
1548+ b'\x00 \x00 \x00 \x00 \x00 \x00 \xf8 ?' )
1549+
1550+ def test_unpack (self ):
1551+ self .assertEqual (_testcapi .float_unpack (b'>\x00 ' , BIG_ENDIAN ),
1552+ 1.5 )
1553+ self .assertEqual (_testcapi .float_unpack (b'?\xc0 \x00 \x00 ' , BIG_ENDIAN ),
1554+ 1.5 )
1555+ self .assertEqual (_testcapi .float_unpack (b'?\xf8 \x00 \x00 \x00 \x00 \x00 \x00 ' , BIG_ENDIAN ),
1556+ 1.5 )
1557+ self .assertEqual (_testcapi .float_unpack (b'\x00 >' , LITTLE_ENDIAN ),
1558+ 1.5 )
1559+ self .assertEqual (_testcapi .float_unpack (b'\x00 \x00 \xc0 ?' , LITTLE_ENDIAN ),
1560+ 1.5 )
1561+ self .assertEqual (_testcapi .float_unpack (b'\x00 \x00 \x00 \x00 \x00 \x00 \xf8 ?' , LITTLE_ENDIAN ),
1562+ 1.5 )
1563+
1564+ def test_roundtrip (self ):
1565+ large = 2.0 ** 100
1566+ values = [1.0 , 1.5 , large , 1.0 / 7 , math .pi ]
1567+ if HAVE_IEEE_754 :
1568+ values .extend ((INF , NAN ))
1569+ for value in values :
1570+ for size in (2 , 4 , 8 ,):
1571+ if size == 2 and value == large :
1572+ # too large for 16-bit float
1573+ continue
1574+ rel_tol = EPSILON [size ]
1575+ for endian in (BIG_ENDIAN , LITTLE_ENDIAN ):
1576+ with self .subTest (value = value , size = size , endian = endian ):
1577+ data = _testcapi .float_pack (size , value , endian )
1578+ value2 = _testcapi .float_unpack (data , endian )
1579+ if isnan (value ):
1580+ self .assertTrue (isnan (value2 ), (value , value2 ))
1581+ elif size < 8 :
1582+ self .assertTrue (math .isclose (value2 , value , rel_tol = rel_tol ),
1583+ (value , value2 ))
1584+ else :
1585+ self .assertEqual (value2 , value )
1586+
1587+
15411588if __name__ == '__main__' :
15421589 unittest .main ()
0 commit comments