tricu

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

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