float_finite_test.py (3621B)
1 #!/usr/bin/env python3 2 import math 3 import struct 4 import sys 5 import unittest 6 7 from float_finite import SIGN, LIMIT, MASK, cases, oracle, finite_value, normalized, special_words 8 from float_quantize import dyadic, BIAS 9 10 11 def host_float(bits): return struct.unpack('>d',bits.to_bytes(8,'big'))[0] 12 def host_bits(x): return int.from_bytes(struct.pack('>d',x),'big') 13 14 15 class FiniteTests(unittest.TestCase): 16 def test_signed_zero_matrix(self): 17 for a in [0,SIGN]: 18 for b in [0,SIGN]: 19 for sub in [False,True]: 20 negative=bool(a) and (bool(b)!=sub) 21 self.assertEqual(oracle(a,b,sub),(True,(SIGN if negative else 0,False,False))) 22 23 def test_cancellation_and_zero_identity(self): 24 for w in special_words(): 25 self.assertEqual(oracle(w,w,True),(True,(0,False,False))) 26 self.assertEqual(oracle(w,w^SIGN),(True,(0,False,False))) 27 if w & MASK: 28 self.assertEqual(oracle(w,0),(True,(w,False,False))) 29 self.assertEqual(oracle(0,w,True),(True,(w^SIGN,False,False))) 30 31 def test_normalization_establishes_invariants(self): 32 words=special_words()+[sign|2**k for sign in [0,SIGN] for k in range(52)] 33 for word in words: 34 sign,scale,m=normalized(word) 35 self.assertEqual((-1 if sign else 1)*dyadic(m,scale-BIAS),finite_value(word)) 36 if m: 37 self.assertTrue(2**52<=m<2**53) 38 self.assertTrue(1178<=scale<=3275) 39 else:self.assertEqual(word & MASK,0) 40 41 def test_subnormal_transition_and_signed_overflow(self): 42 self.assertEqual(oracle(2**52-1,1),(True,(2**52,False,False))) 43 self.assertEqual(oracle(2**52,1,True),(True,(2**52-1,False,False))) 44 self.assertEqual(oracle(SIGN|1,SIGN|1),(True,(SIGN|2,False,False))) 45 self.assertEqual(oracle(0x7fefffffffffffff,0x7fefffffffffffff),(True,(0x7ff0000000000000,True,False))) 46 self.assertEqual(oracle(0xffefffffffffffff,0xffefffffffffffff),(True,(0xfff0000000000000,True,False))) 47 48 def test_rejection_and_precedence(self): 49 for w in [0x7ff0000000000000,0xfff0000000000000,0x7ff0000000000001,0xfff8000000000001]: 50 self.assertEqual(oracle(w,0),(False,1)) 51 self.assertEqual(oracle(0,w,True),(False,1)) 52 self.assertEqual(oracle(w,LIMIT),(False,2)) 53 self.assertEqual(oracle(2**128,0),(False,2)) 54 55 @unittest.skipUnless(sys.float_info.radix==2 and sys.float_info.mant_dig==53 and sys.float_info.rounds==1, 56 'requires nearest-rounding binary64 Python floats') 57 def test_corpus_against_host_and_exact_flags(self): 58 for a,b in cases(): 59 for sub in [False,True]: 60 ok,(bits,inexact,underflow)=oracle(a,b,sub) 61 self.assertTrue(ok) 62 fa,fb=host_float(a),host_float(b) 63 actual=fa-fb if sub else fa+fb 64 self.assertEqual(bits,host_bits(actual),(hex(a),hex(b),sub)) 65 x=finite_value(a)+(-finite_value(b) if sub else finite_value(b)) 66 self.assertEqual(inexact,math.isinf(actual) or finite_value(bits)!=x) 67 # Finite binary64 operands are multiples of the minimum quantum: 68 # an add/sub result in the subnormal range is exactly representable. 69 self.assertFalse(underflow) 70 71 def test_algebraic_sign_relations(self): 72 for a,b in cases()[::17]: 73 self.assertEqual(oracle(a,b),oracle(b,a)) 74 self.assertEqual(oracle(a,b,True),oracle(a,b^SIGN)) 75 76 77 if __name__=='__main__':unittest.main()