rounded-hw-0.1.0.0: test/RoundedArithmeticSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
module RoundedArithmeticSpec where
import Data.Coerce
import Data.Proxy
import qualified Numeric.Rounded.Hardware.Backend.C as Backend.C
import Numeric.Rounded.Hardware.Backend.ViaRational
import Numeric.Rounded.Hardware.Internal
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck
import Util
infix 4 ==.
(==.) :: (RealFloat a, Show a) => a -> a -> Property
(==.) = sameFloatP
prop_roundedAdd :: (RealFloat a, Show a, RoundedRing a) => Proxy a -> a -> a -> Property
prop_roundedAdd _proxy x y =
-- Assume neither x nor y is NaN
let ne = roundedAdd ToNearest x y
ze = roundedAdd TowardZero x y
inf = roundedAdd TowardInf x y
ninf = roundedAdd TowardNegInf x y
in ne ==. x + y .&&. ninf <= inf .&&. (ne ==. ninf .||. ne ==. inf) .&&. (ze ==. ninf .||. ze ==. inf) .&&. abs ze ==. min (abs ninf) (abs inf)
prop_roundedSub :: (RealFloat a, Show a, RoundedRing a) => Proxy a -> a -> a -> Property
prop_roundedSub _proxy x y =
-- Assume neither x nor y is NaN
let ne = roundedSub ToNearest x y
ze = roundedSub TowardZero x y
inf = roundedSub TowardInf x y
ninf = roundedSub TowardNegInf x y
in ne ==. x - y .&&. ninf <= inf .&&. (ne ==. ninf .||. ne ==. inf) .&&. (ze ==. ninf .||. ze ==. inf) .&&. abs ze ==. min (abs ninf) (abs inf)
prop_roundedMul :: (RealFloat a, Show a, RoundedRing a) => Proxy a -> a -> a -> Property
prop_roundedMul _proxy x y =
-- Assume neither x nor y is NaN
let ne = roundedMul ToNearest x y
ze = roundedMul TowardZero x y
inf = roundedMul TowardInf x y
ninf = roundedMul TowardNegInf x y
in ne ==. x * y .&&. ninf <= inf .&&. (ne ==. ninf .||. ne ==. inf) .&&. (ze ==. ninf .||. ze ==. inf) .&&. abs ze ==. min (abs ninf) (abs inf)
prop_roundedDiv :: (RealFloat a, Show a, RoundedFractional a) => Proxy a -> a -> NonZero a -> Property
prop_roundedDiv _proxy x (NonZero y) =
-- Assume neither x nor y is NaN
let ne = roundedDiv ToNearest x y
ze = roundedDiv TowardZero x y
inf = roundedDiv TowardInf x y
ninf = roundedDiv TowardNegInf x y
in ne ==. x / y .&&. ninf <= inf .&&. (ne ==. ninf .||. ne ==. inf) .&&. (ze ==. ninf .||. ze ==. inf) .&&. abs ze ==. min (abs ninf) (abs inf)
prop_roundedSqrt :: (RealFloat a, Show a, RoundedSqrt a) => Proxy a -> a -> Property
prop_roundedSqrt _proxy x =
-- Assume neither x nor y is NaN
let ne = roundedSqrt ToNearest x
ze = roundedSqrt TowardZero x
inf = roundedSqrt TowardInf x
ninf = roundedSqrt TowardNegInf x
in if isNaN x || x < 0
then isNaN ne .&&. isNaN ze .&&. isNaN inf .&&. isNaN ninf
else ne ==. sqrt x .&&. ninf <= inf .&&. (ne ==. ninf .||. ne ==. inf) .&&. (ze ==. ninf .||. ze ==. inf) .&&. abs ze ==. min (abs ninf) (abs inf)
specT :: (RealFloat a, RoundedFractional a, RoundedSqrt a, Arbitrary a, Show a) => Proxy a -> Spec
specT proxy = do
prop "roundedAdd" $ prop_roundedAdd proxy
prop "roundedSub" $ prop_roundedSub proxy
prop "roundedMul" $ prop_roundedMul proxy
prop "roundedDiv" $ prop_roundedDiv proxy
prop "roundedSqrt" $ prop_roundedSqrt proxy
verifyImplementation :: forall base a. (RealFloat base, RoundedFractional a, RoundedSqrt a, Arbitrary base, Show base, RealFloatConstants base, Coercible a base) => Proxy base -> Proxy a -> Spec
verifyImplementation _ _ = do
let unVR (ViaRational x) = x
c :: base -> a
c x = coerce x
prop "roundedAdd" $ \r x y -> unVR (roundedAdd r (ViaRational x) (ViaRational y)) ==. coerce (roundedAdd r (c x) (c y))
prop "roundedSub" $ \r x y -> unVR (roundedSub r (ViaRational x) (ViaRational y)) ==. coerce (roundedSub r (c x) (c y))
prop "roundedMul" $ \r x y -> unVR (roundedMul r (ViaRational x) (ViaRational y)) ==. coerce (roundedMul r (c x) (c y))
prop "roundedDiv" $ \r x y -> unVR (roundedDiv r (ViaRational x) (ViaRational y)) ==. coerce (roundedDiv r (c x) (c y))
prop "roundedSqrt" $ \r x -> unVR (roundedSqrt r (ViaRational x)) ==. coerce (roundedSqrt r (c x))
spec :: Spec
spec = do
describe "Double" $ specT (Proxy :: Proxy Double)
describe "Float" $ specT (Proxy :: Proxy Float)
describe "Double default" $ verifyImplementation (Proxy :: Proxy Double) (Proxy :: Proxy Double)
describe "Float default" $ verifyImplementation (Proxy :: Proxy Float) (Proxy :: Proxy Float)
-- TODO: Disable when `pure-hs` is on
describe "Double C" $ verifyImplementation (Proxy :: Proxy Double) (Proxy :: Proxy Backend.C.CDouble)
describe "Float C" $ verifyImplementation (Proxy :: Proxy Float) (Proxy :: Proxy Backend.C.CFloat)