libBF-0.5.0: src/LibBF.hs
{-# Language BlockArguments #-}
{-# Language Trustworthy #-}
-- | Computation with high-precision floats.
module LibBF
(
-- * Constants
BigFloat
, bfPosZero, bfNegZero
, bfPosInf, bfNegInf
, bfNaN
-- * Conversions
, bfFromWord
, bfFromInt
, bfFromDouble
, bfFromInteger
, bfFromString
, bfToDouble
, bfToString
, bfToRep
, BFRep(..)
, BFNum(..)
-- * Predicates
, bfIsFinite
, bfIsZero
, bfIsNaN
, bfCompare
, bfSign
, bfExponent
, Sign(..)
-- * Arithmetic
, bfNeg
, bfAdd, bfSub, bfMul, bfDiv
, bfMulWord, bfMulInt, bfMul2Exp
, bfSqrt
, bfPow
-- * Rounding
, bfRoundFloat, bfRoundInt
-- * Mutability
, bfUnsafeThaw
, bfUnsafeFreeze
-- * Limits
-- * Configuration
, module LibBF.Opts
) where
import Data.Word
import Data.Int
import System.IO.Unsafe
import LibBF.Mutable as M
import LibBF.Opts
import Control.DeepSeq
-- | Arbitrary precision floating point numbers.
newtype BigFloat = BigFloat BF
instance NFData BigFloat where
rnf x = x `seq` ()
instance Show BigFloat where
show = bfToString 16 (showFreeMin Nothing <> addPrefix)
--------------------------------------------------------------------------------
{-# NOINLINE ctxt #-}
{-# OPTIONS_GHC -fno-cse #-}
ctxt :: BFContext
ctxt = unsafePerformIO newContext
newBigFloat :: (BF -> IO ()) -> BigFloat
newBigFloat f = unsafe $
do bf <- new ctxt
f bf
pure (BigFloat bf)
newBigFloat' :: (BF -> IO a) -> (BigFloat,a)
newBigFloat' f = unsafe $
do bf <- new ctxt
a <- f bf
pure (BigFloat bf, a)
unsafe :: IO a -> a
unsafe = unsafePerformIO
--------------------------------------------------------------------------------
-- Constants
-- | Positive zero.
bfPosZero :: BigFloat
bfPosZero = newBigFloat (setZero Pos)
-- | Negative zero.
bfNegZero :: BigFloat
bfNegZero = newBigFloat (setZero Neg)
-- | Positive infinity.
bfPosInf :: BigFloat
bfPosInf = newBigFloat (setInf Pos)
-- | Negative infinity.
bfNegInf :: BigFloat
bfNegInf = newBigFloat (setInf Neg)
-- | Not-a-number.
bfNaN :: BigFloat
bfNaN = newBigFloat setNaN
-- | A floating point number corresponding to the given word.
bfFromWord :: Word64 -> BigFloat
bfFromWord = newBigFloat . setWord
-- | A floating point number corresponding to the given int.
bfFromInt :: Int64 -> BigFloat
bfFromInt = newBigFloat . setInt
-- | A floating point number corresponding to the given double.
bfFromDouble :: Double -> BigFloat
bfFromDouble = newBigFloat . setDouble
-- | A floating point number corresponding to the given integer.
bfFromInteger :: Integer -> BigFloat
bfFromInteger = newBigFloat . setInteger
-- | IEEE 754 equality
instance Eq BigFloat where
BigFloat x == BigFloat y = unsafe (cmpEq x y)
-- | IEEE 754 comparisons
instance Ord BigFloat where
BigFloat x < BigFloat y = unsafe (cmpLT x y)
BigFloat x <= BigFloat y = unsafe (cmpLEQ x y)
{-| Compare the two numbers. The special values are ordered like this:
* -0 < 0
* NaN == NaN
* NaN is larger than all other numbers
Note that this differs from `(<=)`
-}
bfCompare :: BigFloat -> BigFloat -> Ordering
bfCompare (BigFloat x) (BigFloat y) = unsafe (cmp x y)
-- | Is this a "normal" (i.e., non-infinite, non NaN) number.
bfIsFinite :: BigFloat -> Bool
bfIsFinite (BigFloat x) = unsafe (isFinite x)
-- | Is this value NaN.
bfIsNaN :: BigFloat -> Bool
bfIsNaN (BigFloat x) = unsafe (M.isNaN x)
-- | Get the sign of a number. Assumes the input is not NaN.
bfSign :: BigFloat -> Maybe Sign
bfSign (BigFloat x) = unsafe (getSign x)
-- | Get the exponent for the given number.
-- Infinity, zero and NaN do not have an exponent.
bfExponent :: BigFloat -> Maybe Int64
bfExponent (BigFloat x) = unsafe (getExp x)
-- | Is this value a zero.
bfIsZero :: BigFloat -> Bool
bfIsZero (BigFloat x) = unsafe (isZero x)
-- | Negate a floating point number.
bfNeg :: BigFloat -> BigFloat
bfNeg (BigFloat x) = newBigFloat (\bf -> setBF x bf >> fneg bf)
-- | Add two numbers useing the given options.
bfAdd :: BFOpts -> BigFloat -> BigFloat -> (BigFloat,Status)
bfAdd opt (BigFloat x) (BigFloat y) = newBigFloat' (fadd opt x y)
-- | Subtract two numbers useing the given options.
bfSub :: BFOpts -> BigFloat -> BigFloat -> (BigFloat,Status)
bfSub opt (BigFloat x) (BigFloat y) = newBigFloat' (fsub opt x y)
-- | Multiply two numbers using the given options.
bfMul :: BFOpts -> BigFloat -> BigFloat -> (BigFloat,Status)
bfMul opt (BigFloat x) (BigFloat y) = newBigFloat' (fmul opt x y)
-- | Multiply a number and a word, using the given options.
bfMulWord :: BFOpts -> BigFloat -> Word64 -> (BigFloat,Status)
bfMulWord opt (BigFloat x) y = newBigFloat' (fmulWord opt x y)
-- | Multiply a number and an int, using the given options.
bfMulInt :: BFOpts -> BigFloat -> Int64 -> (BigFloat,Status)
bfMulInt opt (BigFloat x) y = newBigFloat' (fmulInt opt x y)
-- | Multiply a number by @2^e@.
bfMul2Exp :: BFOpts -> BigFloat -> Int64 -> (BigFloat,Status)
bfMul2Exp opt (BigFloat x) e = newBigFloat' (\p ->
do setBF x p
fmul2Exp opt e p)
-- | Divide two numbers useing the given options.
bfDiv :: BFOpts -> BigFloat -> BigFloat -> (BigFloat,Status)
bfDiv opt (BigFloat x) (BigFloat y) = newBigFloat' (fdiv opt x y)
-- | Square root of two numbers useing the given options.
bfSqrt :: BFOpts -> BigFloat -> (BigFloat,Status)
bfSqrt opt (BigFloat x) = newBigFloat' (fsqrt opt x)
-- | Round to a float matching the input parameters.
bfRoundFloat :: BFOpts -> BigFloat -> (BigFloat,Status)
bfRoundFloat opt (BigFloat x) = newBigFloat' (\bf ->
do setBF x bf
fround opt bf
)
-- | Round to an integer using the given parameters.
bfRoundInt :: BFOpts -> BigFloat -> (BigFloat,Status)
bfRoundInt opt (BigFloat x) = newBigFloat' (\bf ->
do setBF x bf
frint opt bf
)
-- | Exponentiate a word to a positive integer power.
bfPow :: BFOpts -> BigFloat -> BigFloat -> (BigFloat, Status)
bfPow opts (BigFloat x) (BigFloat y) = newBigFloat' (fpow opts x y)
-- | Constant to a 'Double'
bfToDouble :: RoundMode -> BigFloat -> (Double, Status)
bfToDouble r (BigFloat x) = unsafe (toDouble r x)
-- | Render as a 'String', using the given settings.
bfToString :: Int {- ^ Base -} -> ShowFmt -> BigFloat -> String
bfToString radix opts (BigFloat x) =
unsafe (toString radix opts x)
-- | Parse a number from the given string.
-- Returns @NaN` if the string does not correspond to a number we recognize.
bfFromString :: Int {- ^ Base -} -> BFOpts -> String -> (BigFloat,Status)
bfFromString radix opts str =
newBigFloat' \bf ->
do (status,_,usedAll) <- setString radix opts str bf
if usedAll
then pure status
else do setNaN bf
pure Ok
-- | The float as an exponentiated 'Integer'.
bfToRep :: BigFloat -> BFRep
bfToRep (BigFloat x) = unsafe (toRep x)
-- | Make a number mutable.
-- WARNING: This does not copy the number,
-- so it could break referential transperancy.
bfUnsafeThaw :: BigFloat -> BF
bfUnsafeThaw (BigFloat x) = x
-- | Make a number immutable.
-- WARNING: This does not copy the number,
-- so it could break referential transperancy.
bfUnsafeFreeze :: BF -> BigFloat
bfUnsafeFreeze = BigFloat
--------------------------------------------------------------------------------