Data.Decimal

Decimal-0.5.2: Decimal numbers with variable precision

Safe Haskell | Safe-Inferred
Language | Haskell2010

Data.Decimal

Contents

- Decimal Values

Description

Decimal numbers are represented as `m*10^(-e)` where `m` and `e` are integers. The exponent `e` is an unsigned Word8. Hence the smallest value that can be represented is `10^-255`.

Unary arithmetic results have the exponent of the argument. Addition and subtraction results have an exponent equal to the maximum of the exponents of the arguments. Other operators have exponents sufficient to show the exact result, up to a limit of 255:

    0.15 * 0.15 :: Decimal    = 0.0225
    (1/3) :: Decimal          = 0.33333333333333...
    decimalPlaces (1/3)       = 255

While `(/)` is defined, you don't normally want to use it. Instead The functions "divide" and "allocate" will split a decimal amount into lists of results which are guaranteed to sum to the original number. This is a useful property when doing financial arithmetic.

The arithmetic on mantissas is always done using `Integer`, regardless of the type of `DecimalRaw` being manipulated. In practice it is strongly recommended that `Decimal` be used, with other types being used only where necessary (e.g. to conform to a network protocol). For instance `(1/3) :: DecimalRaw Int` does not give the right answer.

Care must be taken with literal values of type Decimal. As per the Haskell Report, the literal `10.00` will be converted into `fromRational 10.00`, which in a `Decimal` context will be converted into `10` with zero decimal places. Likewise `10.10` will be converted into `10.1` with one decimal place. If you mean `10.00` with 2 decimal places then you have to write `roundTo 2 10`.

Synopsis

- data DecimalRaw i = Decimal {
  - decimalPlaces :: !Word8
  - decimalMantissa :: !i
  }
- type Decimal = DecimalRaw Integer
- realFracToDecimal :: (Integral i, RealFrac r) => Word8 -> r -> DecimalRaw i
- decimalConvert :: (Integral a, Integral b, Bounded b) => DecimalRaw a -> Maybe (DecimalRaw b)
- unsafeDecimalConvert :: (Integral a, Integral b) => DecimalRaw a -> DecimalRaw b
- roundTo :: Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
- roundTo' :: Integral i => (Rational -> i) -> Word8 -> DecimalRaw i -> DecimalRaw i
- (*.) :: (Integral i, RealFrac r) => DecimalRaw i -> r -> DecimalRaw i
- divide :: Decimal -> Int -> [(Int, Decimal)]
- allocate :: Decimal -> [Integer] -> [Decimal]
- eitherFromRational :: Integral i => Rational -> Either String (DecimalRaw i)
- normalizeDecimal :: Integral i => DecimalRaw i -> DecimalRaw i

## Decimal Values

data DecimalRaw i #

Raw decimal arithmetic type constructor. A decimal value consists of an integer mantissa and a negative exponent which is interpreted as the number of decimal places. The value stored in a `Decimal d` is therefore equal to:

    decimalMantissa d / (10 ^ decimalPlaces d)

The Show instance will add trailing zeros, so `show $ Decimal 3 1500` will return "1.500". Conversely the Read instance will use the decimal places to determine the precision.

Constructors

Decimal |
Fields decimalPlaces :: !Word8 decimalMantissa :: !i

#### Instances

Instances details

NFData i => NFData (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods rnf :: DecimalRaw i -> () #
Integral i => Enum (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods succ :: DecimalRaw i -> DecimalRaw i # pred :: DecimalRaw i -> DecimalRaw i # toEnum :: Int -> DecimalRaw i # fromEnum :: DecimalRaw i -> Int # enumFrom :: DecimalRaw i -> [DecimalRaw i] # enumFromThen :: DecimalRaw i -> DecimalRaw i -> [DecimalRaw i] # enumFromTo :: DecimalRaw i -> DecimalRaw i -> [DecimalRaw i] # enumFromThenTo :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i -> [DecimalRaw i] #
Integral i => Num (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods (+) :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i # (-) :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i # (*) :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i # negate :: DecimalRaw i -> DecimalRaw i # abs :: DecimalRaw i -> DecimalRaw i # signum :: DecimalRaw i -> DecimalRaw i # fromInteger :: Integer -> DecimalRaw i #
(Integral i, Read i) => Read (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods readsPrec :: Int -> ReadS (DecimalRaw i) # readList :: ReadS [DecimalRaw i] # readPrec :: ReadPrec (DecimalRaw i) # readListPrec :: ReadPrec [DecimalRaw i] #
Integral i => Fractional (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods (/) :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i # recip :: DecimalRaw i -> DecimalRaw i # fromRational :: Rational -> DecimalRaw i #
Integral i => Real (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods toRational :: DecimalRaw i -> Rational #
Integral i => RealFrac (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods properFraction :: Integral b => DecimalRaw i -> (b, DecimalRaw i) # truncate :: Integral b => DecimalRaw i -> b # round :: Integral b => DecimalRaw i -> b # ceiling :: Integral b => DecimalRaw i -> b # floor :: Integral b => DecimalRaw i -> b #
(Integral i, Show i) => Show (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods showsPrec :: Int -> DecimalRaw i -> ShowS # show :: DecimalRaw i -> String # showList :: [DecimalRaw i] -> ShowS #
Integral i => Eq (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods (==) :: DecimalRaw i -> DecimalRaw i -> Bool # (/=) :: DecimalRaw i -> DecimalRaw i -> Bool #
Integral i => Ord (DecimalRaw i) # |
Instance details Defined in Data.Decimal Methods compare :: DecimalRaw i -> DecimalRaw i -> Ordering # (<) :: DecimalRaw i -> DecimalRaw i -> Bool # (<=) :: DecimalRaw i -> DecimalRaw i -> Bool # (>) :: DecimalRaw i -> DecimalRaw i -> Bool # (>=) :: DecimalRaw i -> DecimalRaw i -> Bool # max :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i # min :: DecimalRaw i -> DecimalRaw i -> DecimalRaw i #

type Decimal = DecimalRaw Integer #

Arbitrary precision decimal type. Programs should do decimal arithmetic with this type and only convert to other instances of DecimalRaw where required by an external interface. This will avoid issues with integer overflows.

Using this type is also faster because it avoids repeated conversions to and from `Integer`.

realFracToDecimal :: (Integral i, RealFrac r) => Word8 -> r -> DecimalRaw i #

Convert a real fractional value into a Decimal of the appropriate precision.

decimalConvert :: (Integral a, Integral b, Bounded b) => DecimalRaw a -> Maybe (DecimalRaw b) #

Convert a `DecimalRaw` from one base to another. Returns `Nothing` if this would cause arithmetic overflow.

unsafeDecimalConvert :: (Integral a, Integral b) => DecimalRaw a -> DecimalRaw b #

Convert a `DecimalRaw` from one base representation to another. Does not check for overflow in the new representation. Only use after using "roundTo" to put an upper value on the exponent, or to convert to a larger representation.

roundTo :: Integral i => Word8 -> DecimalRaw i -> DecimalRaw i #

Round a `DecimalRaw` to a specified number of decimal places. If the value ends in `5` then it is rounded to the nearest even value (Banker's Rounding)

roundTo' :: Integral i => (Rational -> i) -> Word8 -> DecimalRaw i -> DecimalRaw i #

Round a `DecimalRaw` to a specified number of decimal places using the specified rounding function. Typically this will be one of `floor`, `ceiling`, `truncate` or `round`. Note that `roundTo == roundTo' round`

(*.) :: (Integral i, RealFrac r) => DecimalRaw i -> r -> DecimalRaw i #

Multiply a `DecimalRaw` by a `RealFrac` value.

divide :: Decimal -> Int -> [(Int, Decimal)] #

Divide a `DecimalRaw` value into one or more portions. The portions will be approximately equal, and the sum of the portions is guaranteed to be the original value.

The portions are represented as a list of pairs. The first part of each pair is the number of portions, and the second part is the portion value. Hence 10 dollars divided 3 ways will produce `[(2, 3.33), (1, 3.34)]`.

allocate :: Decimal -> [Integer] -> [Decimal] #

Allocate a `DecimalRaw` value proportionately with the values in a list. The allocated portions are guaranteed to add up to the original value.

Some of the allocations may be zero or negative, but the sum of the list must not be zero. The allocation is intended to be as close as possible to the following:

    let result = allocate d parts
    in all (== d / sum parts) $ zipWith (/) result parts

eitherFromRational :: Integral i => Rational -> Either String (DecimalRaw i) #

Try to convert Rational to Decimal with absolute precision return string with fail description if not converted

normalizeDecimal :: Integral i => DecimalRaw i -> DecimalRaw i #

Reduce the exponent of the decimal number to the minimal possible value