Merge pull request #4823 from Masorubka1/test_float

Update test_float.py from Cpython v3.11.2
This commit is contained in:
Jim Fasarakis-Hilliard
2023-04-02 16:46:52 +00:00
committed by GitHub

199
Lib/test/test_float.py vendored
View File

@@ -8,18 +8,21 @@ import time
import unittest
from test import support
from test.support import import_helper
from test.test_grammar import (VALID_UNDERSCORE_LITERALS,
INVALID_UNDERSCORE_LITERALS)
from math import isinf, isnan, copysign, ldexp
import math
try:
import _testcapi
except ImportError:
_testcapi = None
HAVE_IEEE_754 = float.__getformat__("double").startswith("IEEE")
INF = float("inf")
NAN = float("nan")
have_getformat = hasattr(float, "__getformat__")
requires_getformat = unittest.skipUnless(have_getformat,
"requires __getformat__")
requires_setformat = unittest.skipUnless(hasattr(float, "__setformat__"),
"requires __setformat__")
#locate file with float format test values
test_dir = os.path.dirname(__file__) or os.curdir
@@ -139,6 +142,10 @@ class GeneralFloatCases(unittest.TestCase):
check('123\xbd')
check(' 123 456 ')
check(b' 123 456 ')
# all whitespace (cf. https://github.com/python/cpython/issues/95605)
check('')
check(' ')
check('\t \n')
# non-ascii digits (error came from non-digit '!')
check('\u0663\u0661\u0664!')
@@ -249,6 +256,35 @@ class GeneralFloatCases(unittest.TestCase):
with self.assertRaisesRegex(TypeError, 'keyword argument'):
float(x='3.14')
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_keywords_in_subclass(self):
class subclass(float):
pass
u = subclass(2.5)
self.assertIs(type(u), subclass)
self.assertEqual(float(u), 2.5)
with self.assertRaises(TypeError):
subclass(x=0)
class subclass_with_init(float):
def __init__(self, arg, newarg=None):
self.newarg = newarg
u = subclass_with_init(2.5, newarg=3)
self.assertIs(type(u), subclass_with_init)
self.assertEqual(float(u), 2.5)
self.assertEqual(u.newarg, 3)
class subclass_with_new(float):
def __new__(cls, arg, newarg=None):
self = super().__new__(cls, arg)
self.newarg = newarg
return self
u = subclass_with_new(2.5, newarg=3)
self.assertIs(type(u), subclass_with_new)
self.assertEqual(float(u), 2.5)
self.assertEqual(u.newarg, 3)
def test_is_integer(self):
self.assertFalse((1.1).is_integer())
self.assertTrue((1.).is_integer())
@@ -590,17 +626,8 @@ class GeneralFloatCases(unittest.TestCase):
self.assertEqual(hash(value), object.__hash__(value))
@requires_setformat
@unittest.skipUnless(hasattr(float, "__getformat__"), "requires __getformat__")
class FormatFunctionsTestCase(unittest.TestCase):
def setUp(self):
self.save_formats = {'double':float.__getformat__('double'),
'float':float.__getformat__('float')}
def tearDown(self):
float.__setformat__('double', self.save_formats['double'])
float.__setformat__('float', self.save_formats['float'])
def test_getformat(self):
self.assertIn(float.__getformat__('double'),
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
@@ -609,24 +636,6 @@ class FormatFunctionsTestCase(unittest.TestCase):
self.assertRaises(ValueError, float.__getformat__, 'chicken')
self.assertRaises(TypeError, float.__getformat__, 1)
def test_setformat(self):
for t in 'double', 'float':
float.__setformat__(t, 'unknown')
if self.save_formats[t] == 'IEEE, big-endian':
self.assertRaises(ValueError, float.__setformat__,
t, 'IEEE, little-endian')
elif self.save_formats[t] == 'IEEE, little-endian':
self.assertRaises(ValueError, float.__setformat__,
t, 'IEEE, big-endian')
else:
self.assertRaises(ValueError, float.__setformat__,
t, 'IEEE, big-endian')
self.assertRaises(ValueError, float.__setformat__,
t, 'IEEE, little-endian')
self.assertRaises(ValueError, float.__setformat__,
t, 'chicken')
self.assertRaises(ValueError, float.__setformat__,
'chicken', 'unknown')
BE_DOUBLE_INF = b'\x7f\xf0\x00\x00\x00\x00\x00\x00'
LE_DOUBLE_INF = bytes(reversed(BE_DOUBLE_INF))
@@ -638,36 +647,6 @@ LE_FLOAT_INF = bytes(reversed(BE_FLOAT_INF))
BE_FLOAT_NAN = b'\x7f\xc0\x00\x00'
LE_FLOAT_NAN = bytes(reversed(BE_FLOAT_NAN))
# on non-IEEE platforms, attempting to unpack a bit pattern
# representing an infinity or a NaN should raise an exception.
@requires_setformat
class UnknownFormatTestCase(unittest.TestCase):
def setUp(self):
self.save_formats = {'double':float.__getformat__('double'),
'float':float.__getformat__('float')}
float.__setformat__('double', 'unknown')
float.__setformat__('float', 'unknown')
def tearDown(self):
float.__setformat__('double', self.save_formats['double'])
float.__setformat__('float', self.save_formats['float'])
def test_double_specials_dont_unpack(self):
for fmt, data in [('>d', BE_DOUBLE_INF),
('>d', BE_DOUBLE_NAN),
('<d', LE_DOUBLE_INF),
('<d', LE_DOUBLE_NAN)]:
self.assertRaises(ValueError, struct.unpack, fmt, data)
def test_float_specials_dont_unpack(self):
for fmt, data in [('>f', BE_FLOAT_INF),
('>f', BE_FLOAT_NAN),
('<f', LE_FLOAT_INF),
('<f', LE_FLOAT_NAN)]:
self.assertRaises(ValueError, struct.unpack, fmt, data)
# on an IEEE platform, all we guarantee is that bit patterns
# representing infinities or NaNs do not raise an exception; all else
# is accident (today).
@@ -694,8 +673,9 @@ class IEEEFormatTestCase(unittest.TestCase):
# TODO: RUSTPYTHON
@unittest.expectedFailure
@support.requires_IEEE_754
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
def test_serialized_float_rounding(self):
from _testcapi import FLT_MAX
FLT_MAX = _testcapi.FLT_MAX
self.assertEqual(struct.pack("<f", 3.40282356e38), struct.pack("<f", FLT_MAX))
self.assertEqual(struct.pack("<f", -3.40282356e38), struct.pack("<f", -FLT_MAX))
@@ -735,18 +715,16 @@ class FormatTestCase(unittest.TestCase):
# conversion to string should fail
self.assertRaises(ValueError, format, 3.0, "s")
# other format specifiers shouldn't work on floats,
# in particular int specifiers
for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] +
[chr(x) for x in range(ord('A'), ord('Z')+1)]):
if not format_spec in 'eEfFgGn%':
self.assertRaises(ValueError, format, 0.0, format_spec)
self.assertRaises(ValueError, format, 1.0, format_spec)
self.assertRaises(ValueError, format, -1.0, format_spec)
self.assertRaises(ValueError, format, 1e100, format_spec)
self.assertRaises(ValueError, format, -1e100, format_spec)
self.assertRaises(ValueError, format, 1e-100, format_spec)
self.assertRaises(ValueError, format, -1e-100, format_spec)
# confirm format options expected to fail on floats, such as integer
# presentation types
for format_spec in 'sbcdoxX':
self.assertRaises(ValueError, format, 0.0, format_spec)
self.assertRaises(ValueError, format, 1.0, format_spec)
self.assertRaises(ValueError, format, -1.0, format_spec)
self.assertRaises(ValueError, format, 1e100, format_spec)
self.assertRaises(ValueError, format, -1e100, format_spec)
self.assertRaises(ValueError, format, 1e-100, format_spec)
self.assertRaises(ValueError, format, -1e-100, format_spec)
# issue 3382
self.assertEqual(format(NAN, 'f'), 'nan')
@@ -868,6 +846,11 @@ class RoundTestCase(unittest.TestCase):
self.assertRaises(TypeError, round, NAN, "ceci n'est pas un integer")
self.assertRaises(TypeError, round, -0.0, 1j)
def test_inf_nan_ndigits(self):
self.assertEqual(round(INF, 0), INF)
self.assertEqual(round(-INF, 0), -INF)
self.assertTrue(math.isnan(round(NAN, 0)))
def test_large_n(self):
for n in [324, 325, 400, 2**31-1, 2**31, 2**32, 2**100]:
self.assertEqual(round(123.456, n), 123.456)
@@ -1538,5 +1521,69 @@ class HexFloatTestCase(unittest.TestCase):
self.assertEqual(getattr(f, 'foo', 'none'), 'bar')
# Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
# Test PyFloat_Unpack2(), PyFloat_Unpack4() and PyFloat_Unpack8()
BIG_ENDIAN = 0
LITTLE_ENDIAN = 1
EPSILON = {
2: 2.0 ** -11, # binary16
4: 2.0 ** -24, # binary32
8: 2.0 ** -53, # binary64
}
@unittest.skipIf(_testcapi is None, 'needs _testcapi')
class PackTests(unittest.TestCase):
def test_pack(self):
self.assertEqual(_testcapi.float_pack(2, 1.5, BIG_ENDIAN),
b'>\x00')
self.assertEqual(_testcapi.float_pack(4, 1.5, BIG_ENDIAN),
b'?\xc0\x00\x00')
self.assertEqual(_testcapi.float_pack(8, 1.5, BIG_ENDIAN),
b'?\xf8\x00\x00\x00\x00\x00\x00')
self.assertEqual(_testcapi.float_pack(2, 1.5, LITTLE_ENDIAN),
b'\x00>')
self.assertEqual(_testcapi.float_pack(4, 1.5, LITTLE_ENDIAN),
b'\x00\x00\xc0?')
self.assertEqual(_testcapi.float_pack(8, 1.5, LITTLE_ENDIAN),
b'\x00\x00\x00\x00\x00\x00\xf8?')
def test_unpack(self):
self.assertEqual(_testcapi.float_unpack(b'>\x00', BIG_ENDIAN),
1.5)
self.assertEqual(_testcapi.float_unpack(b'?\xc0\x00\x00', BIG_ENDIAN),
1.5)
self.assertEqual(_testcapi.float_unpack(b'?\xf8\x00\x00\x00\x00\x00\x00', BIG_ENDIAN),
1.5)
self.assertEqual(_testcapi.float_unpack(b'\x00>', LITTLE_ENDIAN),
1.5)
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\xc0?', LITTLE_ENDIAN),
1.5)
self.assertEqual(_testcapi.float_unpack(b'\x00\x00\x00\x00\x00\x00\xf8?', LITTLE_ENDIAN),
1.5)
def test_roundtrip(self):
large = 2.0 ** 100
values = [1.0, 1.5, large, 1.0/7, math.pi]
if HAVE_IEEE_754:
values.extend((INF, NAN))
for value in values:
for size in (2, 4, 8,):
if size == 2 and value == large:
# too large for 16-bit float
continue
rel_tol = EPSILON[size]
for endian in (BIG_ENDIAN, LITTLE_ENDIAN):
with self.subTest(value=value, size=size, endian=endian):
data = _testcapi.float_pack(size, value, endian)
value2 = _testcapi.float_unpack(data, endian)
if isnan(value):
self.assertTrue(isnan(value2), (value, value2))
elif size < 8:
self.assertTrue(math.isclose(value2, value, rel_tol=rel_tol),
(value, value2))
else:
self.assertEqual(value2, value)
if __name__ == '__main__':
unittest.main()