tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

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()