Module

Data.Fixed

This module defines a numeric type Fixed for working with fixed point numbers in base 10. The precision is tracked in the types.

#Fixed

newtype Fixed (precision :: Precision)

A fixed point representation of a real number, with the specified precision.

A value is multiplied by the precision, truncated and stored as a big integer. That is, we approximate the number by numerator/10^precision, storing only the numerator, and carrying the precision around as type information.

The Semiring and associated instances allow us to perform basic arithmetic operations. Unlike Number, addition of Fixed numbers does satisfy the associativity law, but like Number, most of the other laws of the numeric hierarchy classes are not satisfied due to rounding errors.

Instances

#fromInt

fromInt :: forall precision. KnownPrecision precision => Int -> Fixed precision

Create a Fixed representation of an Int.

#fromBigInt

fromBigInt :: forall precision. KnownPrecision precision => BigInt -> Fixed precision

Create a Fixed representation of a BigInt.

#fromNumber

fromNumber :: forall precision. KnownPrecision precision => Number -> Maybe (Fixed precision)

Approximate a Number as a Fixed value with the specified precision.

> fromNumber 0.1234 :: Maybe (Fixed P10000)
(Just (fromNumber 0.1234 :: P10000))

> fromNumber 0.1234 :: Maybe (Fixed P100)
(Just (fromNumber 0.12 :: P100))

When given a finite Number, this function always succeeds: the number is truncated (rounded towards zero) to the closest possible Fixed value. This function only returns Nothing if it is given NaN, or positive or negative infinity.

> fromNumber (1.0 / 0.0) :: Maybe (Fixed P100)
Nothing

#toNumber

toNumber :: forall precision. KnownPrecision precision => Fixed precision -> Number

Convert a Fixed value to a Number.

Note: Overflow is possible here if the numerator is sufficiently large. Consider using toString instead.

#fromString

fromString :: forall precision. KnownPrecision precision => String -> Maybe (Fixed precision)

Parse a fixed-precision number from a string. Any decimal digits which are not representable in the specified precision will be ignored.

> fromString "123.456" :: Maybe (Fixed P1000)
(Just (fromString "123.456" :: P1000))

Where possible, this function should be preferred over fromNumber, since it is exact (whereas fromNumber can only provide an approximation for larger inputs).

> fromString "9007199254740992.5" :: Maybe (Fixed P10)
(Just (fromString "9007199254740992.5" :: P10))

> fromNumber 9007199254740992.5 :: Maybe (Fixed P10)
(Just (fromString "9007199254740992.0" :: P10))

#toString

toString :: forall precision. KnownPrecision precision => Fixed precision -> String

Represent a Fixed value as a string, using all of the decimal places it can represent (based on its precision).

> map toString (fromString "100.5" :: Maybe (Fixed P10))
(Just "100.5")

> map toString (fromString "100.5" :: Maybe (Fixed P100))
(Just "100.50")

#toStringWithPrecision

toStringWithPrecision :: forall precision. KnownPrecision precision => Int -> Fixed precision -> String

Represent a Fixed value as a string, with the given number of decimal places.

> map (toStringWithPrecision 2) (fromString "1234.567" :: Maybe (Fixed P1000))
(Just "1234.56")

If more decimal places are asked for than the type can provide, the extra decimal places will be provided as zeroes.

> map (toStringWithPrecision 3) (fromString "1234.5" :: Maybe (Fixed P10))
(Just "1234.500")

#numerator

numerator :: forall precision. Fixed precision -> BigInt

Extract the numerator from the representation of the number as a fraction.

> map numerator (fromNumber 0.1234 :: Fixed P1000)
(Just fromString "123")

> map numerator (fromNumber 0.1239 :: Fixed P1000)
(Just fromString "123")

#denominator

denominator :: forall precision. KnownPrecision precision => Fixed precision -> BigInt

#floor

floor :: forall precision. KnownPrecision precision => Fixed precision -> Fixed precision

Calculate the largest whole number smaller than or equal to the provided value.

> map floor $ fromNumber 0.1 :: Maybe (Fixed P10)
(Just (fromNumber 0.0 :: P10))

> map floor $ fromNumber 1.0 :: Maybe (Fixed P10)
(Just (fromNumber 1.0 :: P10))

> floor $ fromNumber (-0.1) :: Maybe (Fixed P10)
(Just (fromNumber (-1.0) :: P10))

#ceil

ceil :: forall precision. KnownPrecision precision => Fixed precision -> Fixed precision

Calculate the smallest whole number greater than or equal to the provided value.

> map ceil $ fromNumber 0.1 :: Maybe (Fixed P10)
(Just (fromNumber 1.0 :: P10))

> map ceil $ fromNumber 1.0 :: Maybe (Fixed P10)
(Just (fromNumber 1.0 :: P10))

> map ceil $ fromNumber (-0.1) :: Maybe (Fixed P10)
(Just (fromNumber 0.0 :: P10))

#round

round :: forall precision. KnownPrecision precision => Fixed precision -> Fixed precision

Round the specified value to the nearest whole number.

> map round $ fromNumber 0.1 :: Maybe (Fixed P10)
(Just (fromNumber 0.0 :: P10))

> map round $ fromNumber 0.9 :: Maybe (Fixed P10)
(Just (fromNumber 1.0 :: P10))

> map round $ fromNumber 0.5 :: Maybe (Fixed P10)
(Just (fromNumber 1.0 :: P10))

> map round $ fromNumber (-0.1) :: Maybe (Fixed P10)
(Just (fromNumber 0.0 :: P10))

#rescale

rescale :: forall precision1 precision2. KnownPrecision precision1 => KnownPrecision precision2 => Fixed precision1 -> Fixed precision2

Change the precision of a fixed-point number. If the new precision is less than the old precision, extra decimal places will be lost.

#approxDiv

approxDiv :: forall precision. Warn (Text "This function is deprecated, please use `/` instead") => KnownPrecision precision => Fixed precision -> Fixed precision -> Fixed precision

Division of fixed-precision numbers. This function is deprecated; you should use / from the EuclideanRing instance instead.

#Precision

data Precision

A kind for type-level precision information

#One

data One :: Precision

No decimal places

Instances

#TenTimes

data TenTimes :: Precision -> Precision

One more decimal place

Instances

#P1

type P1 = One

#P10

type P10 = TenTimes P1

One decimal place

#P100

type P100 = TenTimes P10

Two decimal places

#P1000

type P1000 = TenTimes P100

Three decimal places

#P10000

type P10000 = TenTimes P1000

Four decimal places

#P100000

type P100000 = TenTimes P10000

Five decimal places

#P1000000

type P1000000 = TenTimes P100000

Six decimal places

#PProxy

data PProxy (precision :: Precision)

A value-level proxy for a type-level precision.

Constructors

#KnownPrecision

class KnownPrecision (precision :: Precision)  where

Precision which is known, i.e. it can be reflected to a value at runtime, given a PProxy.

reflectPrecision returns a multiple of ten, corresponding to the maximum number of decimal places which can be stored.

> reflectPrecision (PProxy :: PProxy P1000)
1000

Members

Instances

#reflectPrecisionDecimalPlaces

reflectPrecisionDecimalPlaces :: forall precision. KnownPrecision precision => PProxy precision -> Int

Get the number of decimal places associated with a given Precision at the value level.

> reflectPrecisionDecimalPlaces (PProxy :: PProxy P1000)
3

#reifyPrecision

reifyPrecision :: forall r. Int -> (forall precision. KnownPrecision precision => PProxy precision -> r) -> Maybe r

Reify an non-negative integer (a power of ten) as a Precision.

For example

> reifyPrecision 0 reflectPrecision
Just 1
> reifyPrecision 1 reflectPrecision
Just 10
> reifyPrecision 2 reflectPrecision
Just 100
> reifyPrecision (-1) reflectPrecision
Nothing

Modules