brush-strokes-0.1.0.0: src/lib/Math/Float/Utils.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Math.Float.Utils
( FPBits(..)
, nextAfter, succFP, prevFP
)
where
-- base
import Data.Bits
( Bits((.&.), shiftL) )
import Data.Word
( Word32, Word64 )
import GHC.Float
( castFloatToWord32 , castWord32ToFloat
, castDoubleToWord64, castWord64ToDouble
)
--------------------------------------------------------------------------------
class ( RealFloat f, Num b, Bits b ) => FPBits f b | f -> b, b -> f where
toBits :: f -> b
fromBits :: b -> f
-- | Size in bytes.
sizeOf :: Int
instance FPBits Float Word32 where
toBits = castFloatToWord32
fromBits = castWord32ToFloat
sizeOf = 4
instance FPBits Double Word64 where
toBits = castDoubleToWord64
fromBits = castWord64ToDouble
sizeOf = 8
{-# SPECIALISE nextAfter @Float #-}
{-# SPECIALISE nextAfter @Double #-}
{-# INLINEABLE nextAfter #-}
-- | @nextAfter a b@ computes the next floating-point value after @a@
-- in the direction of @b@.
nextAfter :: forall f b. FPBits f b => f -> f -> f
nextAfter a b
| isNaN a
= a
| isNaN b
= b
| a == b
= b
| otherwise
= let !res_bits
| a == 0
, let !sgn_mask = 1 `shiftL` ( sizeOf @f * 8 - 1 )
= ( toBits b .&. sgn_mask ) + 1
| ( a < b ) == ( a > 0 )
= toBits a + 1
| otherwise
= toBits a - 1
in fromBits res_bits
{-# SPECIALISE succFP @Float #-}
{-# SPECIALISE succFP @Double #-}
{-# INLINEABLE succFP #-}
-- | The next floating-point number.
succFP :: forall f b. FPBits f b => f -> f
succFP x = nextAfter x (1/0)
{-# SPECIALISE prevFP @Float #-}
{-# SPECIALISE prevFP @Double #-}
{-# INLINEABLE prevFP #-}
-- | The previous floating-point number.
prevFP :: forall f b. FPBits f b => f -> f
prevFP x = nextAfter x (-1/0)