tricu

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

float_chains_test.py (3502B)


      1 #!/usr/bin/env python3
      2 import hashlib
      3 import os
      4 from pathlib import Path
      5 import subprocess
      6 import unittest
      7 
      8 from float_chains import cases, groups, chain_oracle
      9 from float_ieee import OPS, SIGN, INF, QNAN, MIN_NORMAL, QUIET, LIMIT, nan, oracle
     10 
     11 
     12 def host_trace(reference):
     13     rows=[];expected=[]
     14     for initial,pool,steps in cases():
     15         word=initial
     16         for op,index in steps:
     17             rows.append(f'{op} {word:016x} {pool[index]:016x}\n')
     18             ok,(word,flags)=oracle(word,pool[index],OPS[op]);assert ok
     19             expected.append((word,flags))
     20     raw=''.join(rows).encode()
     21     result=subprocess.run([str(reference)],input=raw,capture_output=True,check=True,timeout=60)
     22     lines=result.stdout.splitlines();assert len(lines)==len(expected)
     23     for line,(word,flags) in zip(lines,expected):
     24         actual,raised=line.split();actual=int(actual,16);raised=int(raised)
     25         assert raised==sum(int(f)<<i for i,f in enumerate(flags))
     26         if nan(word):assert nan(actual) and actual&QUIET
     27         else:assert actual==word
     28     return dict(checked_steps=len(expected),input_sha256=hashlib.sha256(raw).hexdigest(),
     29                 output_sha256=hashlib.sha256(result.stdout).hexdigest(),
     30                 reference_sha256=hashlib.sha256(Path(reference).read_bytes()).hexdigest())
     31 
     32 
     33 class ChainTests(unittest.TestCase):
     34     def test_no_fused_or_reassociated_rounding(self):
     35         row=(0x3ff0000000000000,[0x3ca0000000000000],[(0,0),(0,0)])
     36         self.assertEqual(chain_oracle(row),(0x3ff0000000000000,(False,False,False,False,True)))
     37         # Two separate halfway additions round to one; a fused sum rounds up one ULP.
     38         self.assertNotEqual(chain_oracle(row)[0],0x3ff0000000000001)
     39 
     40     def test_accumulation_retains_prior_exceptions(self):
     41         row=(0x7fefffffffffffff,[0x4000000000000000,0,INF],[(2,0),(2,1),(0,2)])
     42         self.assertEqual(chain_oracle(row),(QNAN,(True,False,True,False,True)))
     43         row=(0x3ff0000000000000,[0,INF],[(3,0),(3,1)])
     44         self.assertEqual(chain_oracle(row),(QNAN,(True,True,False,False,False)))
     45         row=(MIN_NORMAL,[0x3fefffffffffffff,0x4000000000000000],[(2,0),(2,1)])
     46         word,flags=chain_oracle(row)
     47         self.assertGreater(word,MIN_NORMAL);self.assertEqual(flags,(False,False,False,True,True))
     48 
     49     def test_empty_chain_is_bit_exact(self):
     50         for word in [SIGN,INF|1,SIGN|QNAN|123]:
     51             self.assertEqual(chain_oracle((word,[],[])),(word,(False,)*5))
     52 
     53     def test_reused_and_separate_equal_pool_slots(self):
     54         g=groups()
     55         self.assertEqual([chain_oracle(r) for r in g['affine-reuse-16']],
     56                          [chain_oracle(r) for r in g['affine-unique-16']])
     57         self.assertEqual(g['affine-unique-16'][0][2],[(0 if j%2 else 2,j) for j in range(32)])
     58 
     59     def test_valid_domain_and_result_range(self):
     60         for initial,pool,steps in cases():
     61             self.assertTrue(0<=initial<LIMIT)
     62             self.assertTrue(all(0<=word<LIMIT for word in pool))
     63             self.assertTrue(all(0<=op<4 and 0<=index<len(pool) for op,index in steps))
     64             word,flags=chain_oracle((initial,pool,steps))
     65             self.assertTrue(0<=word<LIMIT);self.assertEqual(len(flags),5)
     66 
     67     @unittest.skipUnless(os.environ.get('TRICU_FLOAT_HOST'),'set TRICU_FLOAT_HOST to the C fenv reference')
     68     def test_steps_against_c_fenv(self):
     69         report=host_trace(os.environ['TRICU_FLOAT_HOST'])
     70         self.assertGreater(report['checked_steps'],3000)
     71 
     72 
     73 if __name__=='__main__':unittest.main()