float_near_jam_test.py (3508B)
1 #!/usr/bin/env python3 2 from bisect import bisect_left 3 import math 4 import struct 5 import sys 6 import unittest 7 8 from float_near_jam import cases, jam, reduced 9 from float_aligned_quantize import exact, nearest 10 from float_quantize import BIAS, dyadic, value 11 12 13 class NearJamTests(unittest.TestCase): 14 def test_jam_primitives(self): 15 for n in range(256): 16 for k in range(10): 17 q,r=divmod(n,2**k) 18 self.assertEqual(jam(n,k),q | bool(r)) 19 self.assertLess(abs(dyadic(n,-k)-jam(n,k)),1) 20 if r: self.assertEqual(jam(n,k)%2,1) 21 22 def test_small_precision_rounding_and_inexact(self): 23 checked=0 24 for p in range(2,6): 25 lo,hi=2**(p-1),2**p-1 26 values=[dyadic(m,-6) for m in range(lo)] 27 values += [dyadic(m,e) for e in range(-6,4) for m in range(lo,hi+1)] 28 virtual=dyadic(2**p,3) 29 def rounded(x): 30 i=bisect_left(values,x) 31 if i==len(values): 32 bits=i if x >= (values[-1]+virtual)/2 else i-1 33 elif values[i]==x or i==0: bits=i 34 else: 35 below,above=x-values[i-1],values[i]-x 36 bits=i-1 if below<above or (below==above and (i-1)%2==0) else i 37 inexact=bits==len(values) or values[bits]!=x 38 return bits,inexact,inexact and bits<lo 39 for a in range(lo,hi+1): 40 for b in range(lo,hi+1): 41 for gap in range(2,p+2): 42 aligned=b << (3-gap) if gap<=3 else jam(b,gap-3) 43 for sign in [-1,1]: 44 n=8*a+sign*aligned 45 self.assertGreaterEqual(n.bit_length(),p+2) 46 self.assertLessEqual(n.bit_length(),p+4) 47 for e in range(-p-7,6): 48 actual=dyadic(a,e)+sign*dyadic(b,e-gap) 49 surrogate=dyadic(n,e-3) 50 self.assertEqual(rounded(actual),rounded(surrogate)) 51 checked+=1 52 self.assertGreater(checked,50000) 53 54 def test_binary64_reduced_corpus(self): 55 for row in cases(): 56 if not 2<=row[1]<=54: continue 57 for sub in [False,True]: 58 self.assertEqual(nearest(exact(row,sub)),nearest(reduced(row,sub))) 59 60 def test_truncation_loses_bits_or_flags(self): 61 for row,sub in [((2252,53,2**52,2**52+1),False), 62 ((2252,53,2**52+2,2**52+1),True), 63 ((2252,4,2**52,2**52+1),False)]: 64 s,g,a,b=row 65 wrong=nearest(dyadic(8*a+(-1 if sub else 1)*(b >> (g-3)),s-BIAS-3)) 66 self.assertNotEqual(nearest(exact(row,sub)),wrong) 67 self.assertEqual(nearest(exact(row,sub)),nearest(reduced(row,sub))) 68 69 @unittest.skipUnless(sys.float_info.radix==2 and sys.float_info.mant_dig==53, 70 'requires binary64 Python floats') 71 def test_exact_fraction_oracle_against_host(self): 72 for row in cases(): 73 for sub in [False,True]: 74 x=exact(row,sub) 75 try:f=float(x) 76 except OverflowError:f=math.inf 77 bits=int.from_bytes(struct.pack('>d',f),'big') 78 inexact=math.isinf(f) or value(bits)!=x 79 self.assertEqual(nearest(x),(bits,inexact,inexact and bits<2**52)) 80 81 82 if __name__=='__main__':unittest.main()