Update test_float.py from Cpython v3.11.2 by Masorubka1 · Pull Request #4823 · RustPython/RustPython
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
# non-ascii digits (error came from non-digit '!') check('\u0663\u0661\u0664!')
# 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())
@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'])
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))
# 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).
# 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')
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)
# 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()