module TwoSumSpec where import Data.Coerce import Data.Functor.Identity import Data.Proxy import Numeric.Floating.IEEE import Numeric.Floating.IEEE.Internal import Test.Hspec import Test.Hspec.QuickCheck import Test.QuickCheck import Util (forAllFloats2, sameFloatP) twoProduct_generic :: RealFloat a => a -> a -> (a, a) twoProduct_generic x y = coerce (twoProduct (Identity x) (Identity y)) prop_twoSum :: (RealFloat a, Show a) => Proxy a -> a -> a -> Property prop_twoSum _ x y = exponent x < expMax && exponent y < expMax ==> case twoSum x y of (s, t) -> x + y `sameFloatP` s .&&. (isFinite x && isFinite y && isFinite s ==> isFinite t .&&. toRational x + toRational y === toRational s + toRational t) where (_,expMax) = floatRange x prop_twoProduct :: (RealFloat a, Show a) => Proxy a -> (a -> a -> (a, a)) -> a -> a -> Property prop_twoProduct _ tp x y = case tp x y of (s, t) -> x * y `sameFloatP` s .&&. (isFinite x && isFinite y && isFinite s ==> isFinite t .&&. fromRational (toRational x * toRational y - toRational s) === t) -- The result of twoProduct is not exact if the product underflows {-# NOINLINE spec #-} spec :: Spec spec = modifyMaxSuccess (* 100) $ do describe "Double" $ do let proxy :: Proxy Double proxy = Proxy prop "twoSum" $ forAllFloats2 $ prop_twoSum proxy prop "twoProduct" $ forAllFloats2 $ prop_twoProduct proxy twoProduct prop "twoProduct_generic" $ forAllFloats2 $ prop_twoProduct proxy twoProduct_generic describe "Float" $ do let proxy :: Proxy Float proxy = Proxy prop "twoSum" $ forAllFloats2 $ prop_twoSum proxy prop "twoProduct" $ forAllFloats2 $ prop_twoProduct proxy twoProduct prop "twoProduct_generic" $ forAllFloats2 $ prop_twoProduct proxy twoProduct_generic prop "twoProductFloat_viaDouble" $ forAllFloats2 $ prop_twoProduct proxy twoProductFloat_viaDouble