float_finite_product_quotient_test.py (5746B)
1 #!/usr/bin/env python3 2 from bisect import bisect_left 3 from fractions import Fraction 4 import math 5 import struct 6 import sys 7 import unittest 8 9 from float_finite_product_quotient import SIGN, MASK, LIMIT, cases, raw_cases, oracle, finite_value, negative_controls, overflow_pairs 10 from float_quantize import dyadic 11 12 13 def host_float(bits):return struct.unpack('>d',bits.to_bytes(8,'big'))[0] 14 def host_bits(x):return int.from_bytes(struct.pack('>d',x),'big') 15 16 17 class ProductQuotientTests(unittest.TestCase): 18 def test_negative_controls(self): 19 for name,a,b,divide,wrong in negative_controls(): 20 self.assertNotEqual(wrong,oracle(a,b,divide)) 21 controls=negative_controls() 22 self.assertLess(controls[0][-1][1][0],oracle(controls[0][1],controls[0][2])[1][0]) 23 self.assertGreater(controls[1][-1][1][0],oracle(controls[1][1],controls[1][2])[1][0]) 24 # The dropped-remainder division control returns the same word with inexact=False. 25 _,a,b,divide,wrong=controls[-1] 26 self.assertEqual(wrong[1][0],oracle(a,b,divide)[1][0]) 27 self.assertFalse(wrong[1][1]);self.assertTrue(oracle(a,b,divide)[1][1]) 28 29 def test_signed_zero(self): 30 for a in [0,SIGN]: 31 for b in [1,SIGN|1,0x3ff0000000000000,0xbff0000000000000]: 32 for divide in [False,True]: 33 self.assertEqual(oracle(a,b,divide),(True,((a^b)&SIGN,False,False))) 34 for b in [0,SIGN]: 35 self.assertEqual(oracle(a,b),(True,((a^b)&SIGN,False,False))) 36 self.assertEqual(oracle(a,b,True),(False,3)) 37 38 def test_real_underflow_midpoints(self): 39 half=0x3fe0000000000000;two=0x4000000000000000 40 for sign in [0,SIGN]: 41 for divide,b in [(False,half),(True,two)]: 42 self.assertEqual(oracle(sign|1,b,divide),(True,(sign,True,True))) 43 self.assertEqual(oracle(sign|3,b,divide),(True,(sign|2,True,True))) 44 self.assertEqual(oracle(sign|2,b,divide),(True,(sign|1,False,False))) 45 self.assertEqual(oracle(1,half+1),(True,(1,True,True))) 46 self.assertEqual(oracle(1,half-1),(True,(0,True,True))) 47 self.assertEqual(oracle(1,two-1,True),(True,(1,True,True))) 48 self.assertEqual(oracle(1,two+1,True),(True,(0,True,True))) 49 # The stored-tininess control clears its flag when rounding to minimum normal. 50 self.assertEqual(oracle(2**52,0x3fefffffffffffff),(True,(2**52,True,False))) 51 52 def test_exact_overflow_midpoint(self): 53 pairs=overflow_pairs() 54 a,b=pairs[1] 55 exact=finite_value(a)*finite_value(b) 56 self.assertEqual(exact,(finite_value(0x7fefffffffffffff)+dyadic(1,1024))/2) 57 self.assertEqual(oracle(a,b),(True,(0x7ff0000000000000,True,False))) 58 self.assertLess(oracle(*pairs[0])[1][0],0x7ff0000000000000) 59 self.assertEqual(oracle(*pairs[2])[1][0],0x7ff0000000000000) 60 61 def test_domain_error_priority(self): 62 self.assertEqual(oracle(0x7ff0000000000000,0,True),(False,1)) 63 self.assertEqual(oracle(LIMIT,0,True),(False,2)) 64 self.assertEqual(oracle(1,SIGN,True),(False,3)) 65 66 def test_quotient_recurrence_and_bounds(self): 67 for a,b in raw_cases(): 68 q,r=divmod(a,b) 69 for i in range(56): 70 q,r=2*q,2*r 71 if r>=b:q,r=q+1,r-b 72 self.assertEqual(a<<(i+1),q*b+r) 73 self.assertTrue(0<=r<b) 74 self.assertEqual((q,r),divmod(a<<56,b)) 75 self.assertTrue(56<=q.bit_length()<=57) 76 n=q | bool(r) 77 self.assertLess(abs(Fraction(a<<56,b)-n),1) 78 if r:self.assertEqual(n%2,1) 79 80 def test_exhaustive_small_precision_division_rounding(self): 81 checked=0 82 for p in range(2,7): 83 lo,hi=2**(p-1),2**p-1 84 grid=[dyadic(m,-6) for m in range(lo)] 85 grid += [dyadic(m,e) for e in range(-6,4) for m in range(lo,hi+1)] 86 virtual=dyadic(2**p,3) 87 def rounded(x): 88 i=bisect_left(grid,x) 89 if i==len(grid):bits=i if x>=(grid[-1]+virtual)/2 else i-1 90 elif grid[i]==x or i==0:bits=i 91 else: 92 dl,du=x-grid[i-1],grid[i]-x 93 bits=i-1 if dl<du or (dl==du and (i-1)%2==0) else i 94 ix=bits==len(grid) or grid[bits]!=x 95 return bits,ix,ix and bits<lo 96 for a in range(lo,hi+1): 97 for b in range(lo,hi+1): 98 q,r=divmod(a<<(p+3),b);n=q|bool(r) 99 for e in range(-p-10,p+6): 100 self.assertEqual(rounded(Fraction(a,b)*dyadic(1,e)),rounded(dyadic(n,e-p-3))) 101 checked+=1 102 self.assertEqual(checked,37296) 103 104 @unittest.skipUnless(sys.float_info.radix==2 and sys.float_info.mant_dig==53 and sys.float_info.rounds==1, 105 'requires nearest-rounding binary64 Python floats') 106 def test_full_corpus_against_host(self): 107 for a,b in cases(): 108 for divide in [False,True]: 109 ok,payload=oracle(a,b,divide) 110 if not ok: 111 self.assertTrue(divide and not (b & MASK));self.assertEqual(payload,3);continue 112 bits,inexact,underflow=payload 113 fa,fb=host_float(a),host_float(b) 114 actual=fa/fb if divide else fa*fb 115 self.assertEqual(bits,host_bits(actual),(hex(a),hex(b),divide)) 116 x=finite_value(a)/finite_value(b) if divide else finite_value(a)*finite_value(b) 117 self.assertEqual(inexact,math.isinf(actual) or finite_value(bits)!=x) 118 self.assertEqual(underflow,inexact and (bits&MASK)<2**52) 119 120 121 if __name__=='__main__':unittest.main()