254 lines
9.8 KiB
Python
254 lines
9.8 KiB
Python
|
|
import unittest, struct
|
|
import os
|
|
from test import test_support
|
|
|
|
def isinf(x):
|
|
return x * 0.5 == x
|
|
|
|
def isnan(x):
|
|
return x != x
|
|
|
|
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.assert_(float.__getformat__('double') in
|
|
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
|
|
self.assert_(float.__getformat__('float') in
|
|
['unknown', 'IEEE, big-endian', 'IEEE, little-endian'])
|
|
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))
|
|
BE_DOUBLE_NAN = b'\x7f\xf8\x00\x00\x00\x00\x00\x00'
|
|
LE_DOUBLE_NAN = bytes(reversed(BE_DOUBLE_NAN))
|
|
|
|
BE_FLOAT_INF = b'\x7f\x80\x00\x00'
|
|
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.
|
|
|
|
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).
|
|
# let's also try to guarantee that -0.0 and 0.0 don't get confused.
|
|
|
|
class IEEEFormatTestCase(unittest.TestCase):
|
|
if float.__getformat__("double").startswith("IEEE"):
|
|
def test_double_specials_do_unpack(self):
|
|
for fmt, data in [('>d', BE_DOUBLE_INF),
|
|
('>d', BE_DOUBLE_NAN),
|
|
('<d', LE_DOUBLE_INF),
|
|
('<d', LE_DOUBLE_NAN)]:
|
|
struct.unpack(fmt, data)
|
|
|
|
if float.__getformat__("float").startswith("IEEE"):
|
|
def test_float_specials_do_unpack(self):
|
|
for fmt, data in [('>f', BE_FLOAT_INF),
|
|
('>f', BE_FLOAT_NAN),
|
|
('<f', LE_FLOAT_INF),
|
|
('<f', LE_FLOAT_NAN)]:
|
|
struct.unpack(fmt, data)
|
|
|
|
if float.__getformat__("double").startswith("IEEE"):
|
|
def test_negative_zero(self):
|
|
import math
|
|
def pos_pos():
|
|
return 0.0, math.atan2(0.0, -1)
|
|
def pos_neg():
|
|
return 0.0, math.atan2(-0.0, -1)
|
|
def neg_pos():
|
|
return -0.0, math.atan2(0.0, -1)
|
|
def neg_neg():
|
|
return -0.0, math.atan2(-0.0, -1)
|
|
self.assertEquals(pos_pos(), neg_pos())
|
|
self.assertEquals(pos_neg(), neg_neg())
|
|
|
|
class FormatTestCase(unittest.TestCase):
|
|
def test_format(self):
|
|
# these should be rewritten to use both format(x, spec) and
|
|
# x.__format__(spec)
|
|
|
|
self.assertEqual(format(0.0, 'f'), '0.000000')
|
|
|
|
# the default is 'g', except for empty format spec
|
|
self.assertEqual(format(0.0, ''), '0.0')
|
|
self.assertEqual(format(0.01, ''), '0.01')
|
|
self.assertEqual(format(0.01, 'g'), '0.01')
|
|
|
|
|
|
self.assertEqual(format(1.0, 'f'), '1.000000')
|
|
|
|
self.assertEqual(format(-1.0, 'f'), '-1.000000')
|
|
|
|
self.assertEqual(format( 1.0, ' f'), ' 1.000000')
|
|
self.assertEqual(format(-1.0, ' f'), '-1.000000')
|
|
self.assertEqual(format( 1.0, '+f'), '+1.000000')
|
|
self.assertEqual(format(-1.0, '+f'), '-1.000000')
|
|
|
|
# % formatting
|
|
self.assertEqual(format(-1.0, '%'), '-100.000000%')
|
|
|
|
# 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)
|
|
|
|
class ReprTestCase(unittest.TestCase):
|
|
def test_repr(self):
|
|
floats_file = open(os.path.join(os.path.split(__file__)[0],
|
|
'floating_points.txt'))
|
|
for line in floats_file:
|
|
line = line.strip()
|
|
if not line or line.startswith('#'):
|
|
continue
|
|
v = eval(line)
|
|
self.assertEqual(v, eval(repr(v)))
|
|
floats_file.close()
|
|
|
|
# Beginning with Python 2.6 float has cross platform compatible
|
|
# ways to create and representate inf and nan
|
|
class InfNanTest(unittest.TestCase):
|
|
def test_inf_from_str(self):
|
|
self.assert_(isinf(float("inf")))
|
|
self.assert_(isinf(float("+inf")))
|
|
self.assert_(isinf(float("-inf")))
|
|
|
|
self.assertEqual(repr(float("inf")), "inf")
|
|
self.assertEqual(repr(float("+inf")), "inf")
|
|
self.assertEqual(repr(float("-inf")), "-inf")
|
|
|
|
self.assertEqual(repr(float("INF")), "inf")
|
|
self.assertEqual(repr(float("+Inf")), "inf")
|
|
self.assertEqual(repr(float("-iNF")), "-inf")
|
|
|
|
self.assertEqual(str(float("inf")), "inf")
|
|
self.assertEqual(str(float("+inf")), "inf")
|
|
self.assertEqual(str(float("-inf")), "-inf")
|
|
|
|
self.assertRaises(ValueError, float, "info")
|
|
self.assertRaises(ValueError, float, "+info")
|
|
self.assertRaises(ValueError, float, "-info")
|
|
self.assertRaises(ValueError, float, "in")
|
|
self.assertRaises(ValueError, float, "+in")
|
|
self.assertRaises(ValueError, float, "-in")
|
|
|
|
def test_inf_as_str(self):
|
|
self.assertEqual(repr(1e300 * 1e300), "inf")
|
|
self.assertEqual(repr(-1e300 * 1e300), "-inf")
|
|
|
|
self.assertEqual(str(1e300 * 1e300), "inf")
|
|
self.assertEqual(str(-1e300 * 1e300), "-inf")
|
|
|
|
def test_nan_from_str(self):
|
|
self.assert_(isnan(float("nan")))
|
|
self.assert_(isnan(float("+nan")))
|
|
self.assert_(isnan(float("-nan")))
|
|
|
|
self.assertEqual(repr(float("nan")), "nan")
|
|
self.assertEqual(repr(float("+nan")), "nan")
|
|
self.assertEqual(repr(float("-nan")), "nan")
|
|
|
|
self.assertEqual(repr(float("NAN")), "nan")
|
|
self.assertEqual(repr(float("+NAn")), "nan")
|
|
self.assertEqual(repr(float("-NaN")), "nan")
|
|
|
|
self.assertEqual(str(float("nan")), "nan")
|
|
self.assertEqual(str(float("+nan")), "nan")
|
|
self.assertEqual(str(float("-nan")), "nan")
|
|
|
|
self.assertRaises(ValueError, float, "nana")
|
|
self.assertRaises(ValueError, float, "+nana")
|
|
self.assertRaises(ValueError, float, "-nana")
|
|
self.assertRaises(ValueError, float, "na")
|
|
self.assertRaises(ValueError, float, "+na")
|
|
self.assertRaises(ValueError, float, "-na")
|
|
|
|
def test_nan_as_str(self):
|
|
self.assertEqual(repr(1e300 * 1e300 * 0), "nan")
|
|
self.assertEqual(repr(-1e300 * 1e300 * 0), "nan")
|
|
|
|
self.assertEqual(str(1e300 * 1e300 * 0), "nan")
|
|
self.assertEqual(str(-1e300 * 1e300 * 0), "nan")
|
|
|
|
def test_main():
|
|
test_support.run_unittest(
|
|
FormatFunctionsTestCase,
|
|
UnknownFormatTestCase,
|
|
IEEEFormatTestCase,
|
|
FormatTestCase,
|
|
ReprTestCase,
|
|
InfNanTest,
|
|
)
|
|
|
|
if __name__ == '__main__':
|
|
test_main()
|