deka-0.6.0.0: lib/Deka/Internal/Dec/Ctx.hs
{-# LANGUAGE Safe, OverloadedStrings #-}
module Deka.Internal.Dec.Ctx where
import qualified Data.ByteString.Char8 as BS8
import Deka.Internal.Context
import Deka.Internal.Mpdec
import Deka.Internal.Util.Ctx
import Data.String
-- | Converts a character string to a 'Dec'. Implements the
-- _to-number_ conversion from the General Decimal Arithmetic
-- specification.
--
-- The conversion is exact provided that the numeric string has no
-- more significant digits than are specified in the 'Precision' in
-- the 'Ctx' and the adjusted exponent is in the range specified by
-- 'Emin' and 'Emax' in the 'Ctx'. If there are more than
-- 'Precision' digits in the string, or the exponent is out of
-- range, the value will be rounded as necessary using the 'Round'
-- rounding mode. The 'Precision' therefore both determines the
-- maximum precision for unrounded numbers and defines the minimum
-- size of the 'Dec' structure required.
--
-- Possible errors are 'conversionSyntax' (the string does not have
-- the syntax of a number, which depends on 'setExtended' in the
-- 'Ctx'), 'overflow' (the adjusted exponent of the number is larger
-- than 'Emax'), or 'underflow' (the adjusted exponent is less than
-- 'Emin' and the conversion is not exact). If any of these
-- conditions are set, the number structure will have a defined
-- value as described in the arithmetic specification (this may be a
-- subnormal or infinite value).
fromByteString :: BS8.ByteString -> Ctx Dec
fromByteString bs = Ctx $ \pCtx ->
newDec $ \dn ->
BS8.useAsCString bs $ \cstr ->
c'mpd_set_string dn cstr pCtx
-- | Returns the absolute value. The same effect as 'plus' unless
-- the operand is negative, in which case it is the same as 'minus'.
abs :: Dec -> Ctx Dec
abs = unary c'mpd_abs
-- | Addition.
add :: Dec -> Dec -> Ctx Dec
add = binary c'mpd_add
-- | Digit-wise logical @and@.
and :: Dec -> Dec -> Ctx Dec
and = binary c'mpd_and
-- | @compare x y@ returns @-1@ if a is less than b, 0 if a is equal
-- to b, and 1 if a is greater than b. 'invalidOperation' is set if
-- at least one of the operands is a signaling NaN.
compare :: Dec -> Dec -> Ctx Dec
compare = binary c'mpd_compare
-- | Identical to 'Deka.Dec.compare' except that all NaNs
-- (including quiet NaNs) set the 'invalidOperation' condition.
compareSignal :: Dec -> Dec -> Ctx Dec
compareSignal = binary c'mpd_compare_signal
-- | Division.
divide :: Dec -> Dec -> Ctx Dec
divide = binary c'mpd_div
-- | Returns the integer part of the result of division. It must be
-- possible to express the result as an integer. That is, it must
-- have no more digits than 'Precision' in the 'Ctx'. If it does
-- then 'divisionImpossible' is raised.
divideInteger :: Dec -> Dec -> Ctx Dec
divideInteger = binary c'mpd_divint
-- | Exponentiation. Result is rounded if necessary using the
-- 'Precision' in the 'Ctx' and using the 'roundHalfEven' rounding
-- method.
--
-- Finite results will always be full precision and inexact, except
-- when rhs is a zero or -Infinity (giving 1 or 0 respectively).
-- Inexact results will almost always be correctly rounded, but may
-- be up to 1 ulp (unit in last place) in error in rare cases.
--
-- This is a mathematical function; the @10 ^ 6@ restrictions on
-- precision and range apply as described above.
exp :: Dec -> Ctx Dec
exp = unary c'mpd_exp
-- | @fma x y z@ multiplies @x@ by @y@ and then adds @z@ to that
-- intermediate result. It is equivalent to a multiplication
-- followed by an addition except that the intermediate result is
-- not rounded and will not cause overflow or underflow. That is,
-- only the final result is rounded and checked.
--
-- This is a mathematical function; the @10 ^ 6@ restrictions on
-- precision and range apply as described above.
fma :: Dec -> Dec -> Dec -> Ctx Dec
fma = ternary c'mpd_fma
-- | Digit-wise inversion (a @0@ becomes a @1@ and vice versa).
invert :: Dec -> Ctx Dec
invert = unary c'mpd_invert
-- | Natural logarithm. Results are correctly rounded if
-- 'setAllCorrectRound' is True.
ln :: Dec -> Ctx Dec
ln = unary c'mpd_ln
-- | Returns the adjusted exponent of the operand, according to the
-- rules for @logB@ of IEEE 754. This returns the exponent of the
-- operand as though its decimal point had been moved to follow the
-- first digit while keeping the same value. The result is not
-- limited by 'Emin' or 'Emax'.
-- | If operand is an NaN, the general rules apply. If operand is
-- infinite, the result is +Infinity. If operand is zero, result is
-- -Infinity and 'invalidOperation' is set. Otherwise, the result
-- is the same as the adjusted exponent of the operand, or
-- @floor(log10(a))@ where @a@ is the operand.
logB :: Dec -> Ctx Dec
logB = unary c'mpd_logb
-- | Base 10 logarithm. Results are correctly rounded if
-- 'setAllCorrectRound' is True.
log10 :: Dec -> Ctx Dec
log10 = unary c'mpd_log10
-- | Compares two numbers numerically and returns the larger. If
-- the numbers compare equal then number is chosen with regard to
-- sign and exponent. Unusually, if one operand is a quiet NaN and
-- the other a number, then the number is returned.
max :: Dec -> Dec -> Ctx Dec
max = binary c'mpd_max
-- | Compares the magnitude of two numbers numerically and sets
-- number to the larger. It is identical to 'Deka.Dec.max' except
-- that the signs of the operands are ignored and taken to be 0
-- (non-negative).
maxMag :: Dec -> Dec -> Ctx Dec
maxMag = binary c'mpd_max_mag
-- | Compares two numbers numerically and sets number to the
-- smaller. If the numbers compare equal then number is chosen with
-- regard to sign and exponent. Unusually, if one operand is a quiet
-- NaN and the other a number, then the number is returned.
min :: Dec -> Dec -> Ctx Dec
min = binary c'mpd_min
-- | Compares the magnitude of two numbers numerically and sets
-- number to the smaller. It is identical to 'Deka.Dec.min' except
-- that the signs of the operands are ignored and taken to be 0
-- (non-negative).
minMag :: Dec -> Dec -> Ctx Dec
minMag = binary c'mpd_min_mag
-- | Returns the result of subtracting the operand from zero. hat
-- is, it is negated, following the usual arithmetic rules; this may
-- be used for implementing a prefix minus operation.
minus :: Dec -> Ctx Dec
minus = unary c'mpd_minus
-- | Multiplication.
multiply :: Dec -> Dec -> Ctx Dec
multiply = binary c'mpd_mul
-- | Digit-wise logical inclusive or.
or :: Dec -> Dec -> Ctx Dec
or = binary c'mpd_or
-- | Returns the result of adding the operand to zero. This takes
-- place according to the settings given in the 'Ctx', following the
-- usual arithmetic rules. This may therefore be used for rounding
-- or for implementing a prefix plus operation.
plus :: Dec -> Ctx Dec
plus = unary c'mpd_plus
-- | @power b e@ returns @b@ raised to the power of @e@. Integer
-- powers are exact, provided that the result is finite and fits
-- into 'Precision'.
--
-- Results are not correctly rounded, even if 'setAllCorrectRound'
-- is True. The error of the function is less than @1ULP + t@,
-- where @t@ has a maximum of @0.1ULP@, but is almost always less
-- than @0.001ULP@.
power :: Dec -> Dec -> Ctx Dec
power = binary c'mpd_pow
-- | @quantize a b@ returns the number that is equal in value to
-- @a@, but has the exponent of @b@.
quantize :: Dec -> Dec -> Ctx Dec
quantize = binary c'mpd_quantize
-- overflow/underflow checks, returns @a@ in its simplest form with
-- all trailing zeros removed.
reduce :: Dec -> Ctx Dec
reduce = unary c'mpd_reduce
-- | @remainder a b@ returns the remainder of @a / b@.
remainder :: Dec -> Dec -> Ctx Dec
remainder = binary c'mpd_rem
-- | @remainderNear a b@ returns @a - b * n@, where @n@ is the
-- integer nearest the exact value of @a / b@. If two integers are
-- equally near then the even one is chosen.
remainderNear :: Dec -> Dec -> Ctx Dec
remainderNear = binary c'mpd_rem_near
-- | @rescale a b@ returns the number that is equal in value
-- to @a@, but has the exponent @b@. Special numbers are copied
-- without signaling. This function is not part of the General
-- Decimal Arithmetic Specification. It
-- is also not equivalent to the rescale function that was removed
-- from the specification.
rescale :: Dec -> Signed -> Ctx Dec
rescale a b = Ctx $ \p -> newDec $ \r ->
withDec a $ \pa ->
c'mpd_rescale r pa b p
-- | @rotate x y@ returns @x@ rotated by @y@ places. @y@ must be in
-- the range [-'Precision', 'Precision']. A negative @y@ indicates a
-- right rotation, a positive @y@ a left rotation.
rotate :: Dec -> Dec -> Ctx Dec
rotate = binary c'mpd_rotate
-- | @scaleB a b@ - b must be an integer with exponent 0. If @a@ is
-- infinite, returns @a@. Otherwise, returns @a@ with the
-- value of @b@ added to the exponent.
scaleB :: Dec -> Dec -> Ctx Dec
scaleB = binary c'mpd_scaleb
-- | @shift a b@ returns @a@ shifted by @b@ places. @b@ must be in
-- the range [-'Precision', 'Precision']. A negative @b@ indicates a
-- right shift, a positive @b@ a left shift. Digits that do not fit
-- are discarded.
shift :: Dec -> Dec -> Ctx Dec
shift = binary c'mpd_shift
-- | Returns the square root. This function is always correctly
-- rounded using the 'roundHalfEven' method.
squareRoot :: Dec -> Ctx Dec
squareRoot = unary c'mpd_sqrt
-- | Returns the reciprocal of the square root. This function
-- always uses 'roundHalfEven'. Results are not correctly rounded
-- even if 'setAllCorrectRound' is True.
inverseSquareRoot :: Dec -> Ctx Dec
inverseSquareRoot = unary c'mpd_invroot
-- | Subtraction.
subtract :: Dec -> Dec -> Ctx Dec
subtract = binary c'mpd_sub
-- | Round to an integer, using the rounding mode of the context.
-- Only a signaling NaN causes an 'invalidOperation'
-- condition.
toIntegralExact :: Dec -> Ctx Dec
toIntegralExact = unary c'mpd_round_to_intx
-- | Like 'toIntegralExact', but 'inexact' and 'rounded' are never
-- set.
toIntegralValue :: Dec -> Ctx Dec
toIntegralValue = unary c'mpd_round_to_int
floor :: Dec -> Ctx Dec
floor = unary c'mpd_floor
ceiling :: Dec -> Ctx Dec
ceiling = unary c'mpd_ceil
truncate :: Dec -> Ctx Dec
truncate = unary c'mpd_trunc
-- | Digit-wise logical exclusive or.
xor :: Dec -> Dec -> Ctx Dec
xor = binary c'mpd_xor
-- | Returns the closest representable number that is smaller than
-- the operand.
nextMinus :: Dec -> Ctx Dec
nextMinus = unary c'mpd_next_minus
-- | Returns the closest representable number that is larger than
-- the operand.
nextPlus :: Dec -> Ctx Dec
nextPlus = unary c'mpd_next_plus
-- | @nextToward a b@ returns the representable number closest to
-- @a@ in the direction of @b@.
nextToward :: Dec -> Dec -> Ctx Dec
nextToward = binary c'mpd_next_toward
toBool :: Integral a => a -> Bool
toBool i
| i == 0 = False
| otherwise = True
-- | False if the decimal is special or zero, or the exponent is
-- less than 'Emin'. True otherwise.
isNormal :: Dec -> Ctx Bool
isNormal d = Ctx $ \p ->
withDec d $ \pd ->
c'mpd_isnormal pd p >>= \i ->
return (toBool i)
-- | False if the decimal is special or zero, or the exponent is
-- greater or equal to 'Emin'. True otherwise.
isSubnormal :: Dec -> Ctx Bool
isSubnormal d = Ctx $ \p ->
withDec d $ \pd ->
c'mpd_issubnormal pd p >>= \i ->
return (toBool i)
data PosNeg = Pos | Neg
deriving (Eq, Ord, Show)
data Number
= Infinity
| Normal
| Subnormal
| Zero
deriving (Eq, Ord, Show)
data Class
= SNaN
| NaN
| Number PosNeg Number
deriving (Eq, Ord, Show)
strToClass :: IsString a => [(a, Class)]
strToClass =
[ ("sNaN", SNaN)
, ("NaN", NaN)
, ("-Infinity", Number Neg Infinity)
, ("-Normal", Number Neg Normal)
, ("-Subnormal", Number Neg Subnormal)
, ("-Zero", Number Neg Zero)
, ("+Zero", Number Pos Zero)
, ("+Subnormal", Number Pos Subnormal)
, ("+Normal", Number Pos Normal)
, ("+Infinity", Number Pos Infinity)
]
-- | Determines the 'Class' of a 'Dec'.
numClass :: Dec -> Ctx Class
numClass d = Ctx $ \pCtx ->
withDec d $ \pd ->
c'mpd_class pd pCtx >>= \chars ->
BS8.packCString chars >>= \bs ->
return . maybe (error "numClass: class not found") id
. lookup bs $ strToClass