{-# language FlexibleInstances #-} ----------------------------------------------------------------------------- -- | -- Copyright : (C) 2016 Marco Zocca, 2012-2015 Edward Kmett -- License : GPL-3 (see LICENSE) -- Maintainer : zocca.marco gmail -- Stability : provisional -- Portability : portable -- -- Testing for values "near" zero ----------------------------------------------------------------------------- module Numeric.Eps ( Epsilon(..), isNz, roundZero, roundOne, roundZeroOne, nearOne ) where import Data.Complex import Foreign.C.Types (CFloat, CDouble) -- | Provides a test to see if a quantity is near zero. -- -- >>> nearZero (1e-11 :: Double) -- False -- -- >>> nearZero (1e-17 :: Double) -- True -- -- >>> nearZero (1e-5 :: Float) -- False -- -- >>> nearZero (1e-7 :: Float) -- True class (Floating a, Num a) => Epsilon a where -- | Determine if a quantity is near zero. nearZero :: a -> Bool -- | @'abs' a '<=' 1e-6@ instance Epsilon Float where nearZero a = abs a <= 1e-6 -- | @'abs' a '<=' 1e-12@ instance Epsilon Double where nearZero a = abs a <= 1e-12 -- | @'abs' a '<=' 1e-6@ instance Epsilon CFloat where nearZero a = abs a <= 1e-6 -- | @'abs' a '<=' 1e-12@ instance Epsilon CDouble where nearZero a = abs a <= 1e-12 -- | @'magnitude' a '<=' 1e-6@ instance Epsilon (Complex Float) where nearZero a = magnitude a <= 1e-6 -- | @'magnitude' a '<=' 1e-12@ instance Epsilon (Complex Double) where nearZero a = magnitude a <= 1e-12 -- | @'magnitude' a '<=' 1e-6@ instance Epsilon (Complex CFloat) where nearZero a = magnitude a <= 1e-6 -- | @'magnitude' a '<=' 1e-12@ instance Epsilon (Complex CDouble) where nearZero a = magnitude a <= 1e-12 -- * Rounding operations -- | Is this quantity close to 1 ? nearOne :: Epsilon a => a -> Bool nearOne x = nearZero (1 - x) -- | Is this quantity distinguishable from 0 ? isNz :: Epsilon a => a -> Bool isNz x = not (nearZero x) withDefault :: (t -> Bool) -> t -> t -> t withDefault q d x | q x = d | otherwise = x roundZero, roundOne, roundZeroOne :: Epsilon a => a -> a roundZero = withDefault nearZero (fromIntegral (0 :: Int)) roundOne = withDefault nearOne (fromIntegral (1 :: Int)) with2Defaults :: (t -> Bool) -> (t -> Bool) -> t -> t -> t -> t with2Defaults q1 q2 d1 d2 x | q1 x = d1 | q2 x = d2 | otherwise = x -- | Round to respectively 0 or 1 roundZeroOne = with2Defaults nearZero nearOne (fromIntegral (0 :: Int)) (fromIntegral (1 :: Int))