File: Fixed.hs

package info (click to toggle)
haskell-numbers 3000.2.0.2-7
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 132 kB
  • sloc: haskell: 948; makefile: 3
file content (165 lines) | stat: -rw-r--r-- 5,236 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
{-# LANGUAGE
    EmptyDataDecls,
    GeneralizedNewtypeDeriving,
    ScopedTypeVariables,
    Rank2Types #-}

-- | Numbers with a fixed number of decimals.
module Data.Number.Fixed(
    Fixed,
    Epsilon, Eps1, EpsDiv10, Prec10, Prec50, PrecPlus20,
    Prec500, convertFixed, dynamicEps, precision, with_added_precision) where
import Numeric
import Data.Char
import Data.Ratio
import qualified Data.Number.FixedFunctions as F

-- | The 'Epsilon' class contains the types that can be used to determine the
-- precision of a 'Fixed' number.
class Epsilon e where
    eps :: e -> Rational

-- | An epsilon of 1, i.e., no decimals.
data Eps1
instance Epsilon Eps1 where
    eps _ = 1

-- | A type construct that gives one more decimals than the argument.
data EpsDiv10 p
instance (Epsilon e) => Epsilon (EpsDiv10 e) where
    eps e = eps (un e) / 10
       where un :: EpsDiv10 e -> e
             un = undefined

-- | Ten decimals.
data Prec10
instance Epsilon Prec10 where
    eps _ = 1e-10

-- | 50 decimals.
data Prec50
instance Epsilon Prec50 where
    eps _ = 1e-50

-- | 500 decimals.
data Prec500
instance Epsilon Prec500 where
    eps _ = 1e-500

-- A type that gives 20 more decimals than the argument.
data PrecPlus20 e
instance (Epsilon e) => Epsilon (PrecPlus20 e) where
    eps e = 1e-20 * eps (un e)
       where un :: PrecPlus20 e -> e
             un = undefined

-----------

-- The type of fixed precision numbers.  The type /e/ determines the precision.
newtype Fixed e = F Rational deriving (Eq, Ord, Enum, Real, RealFrac)

-- Get the accuracy (the epsilon) of the type.
precision :: (Epsilon e) => Fixed e -> Rational
precision = getEps

instance (Epsilon e) => Num (Fixed e) where
    (+) = lift2 (+)
    (-) = lift2 (-)
    (*) = lift2 (*)
    negate (F x) = F (negate x)
    abs (F x) = F (abs x)
    signum (F x) = F (signum x)
    fromInteger = F . fromInteger

instance (Epsilon e) => Fractional (Fixed e) where
    (/) = lift2 (/)
    fromRational x = r
        where r = F $ approx x (getEps r)

lift2 :: (Epsilon e) => (Rational -> Rational -> Rational) -> Fixed e -> Fixed e -> Fixed e
lift2 op fx@(F x) (F y) = F $ approx (x `op` y) (getEps fx)

approx :: Rational -> Rational -> Rational
approx x eps = approxRational x (eps/2)

-- | Convert between two arbitrary fixed precision types.
convertFixed :: (Epsilon e, Epsilon f) => Fixed e -> Fixed f
convertFixed e@(F x) = f
  where f = F $ if feps > eeps then approx x feps else x
        feps = getEps f
        eeps = getEps e

getEps :: (Epsilon e) => Fixed e -> Rational
getEps = eps . un
  where un :: Fixed e -> e
        un = undefined

instance (Epsilon e) => Show (Fixed e) where
    showsPrec = showSigned showFixed
      where showFixed f@(F x) = showString $ show q ++ "." ++ decimals r e
              where q :: Integer
                    (q, r) = properFraction (x + e/2)
                    e = getEps f
            decimals a e | e >= 1 = ""
                         | otherwise = intToDigit b : decimals c (10 * e)
                              where (b, c) = properFraction (10 * a)

instance (Epsilon e) => Read (Fixed e) where
    readsPrec _ = readSigned readFixed
      where readFixed s = [ (toFixed0 (approxRational x), s') | (x, s') <- readFloat s ]

instance (Epsilon e) => Floating (Fixed e) where
    pi = toFixed0 F.pi
    sqrt = toFixed1 F.sqrt
    exp x = with_added_precision r (convertFixed . (toFixed1 F.exp)) x where
      r = if x < 0 then 1 else 0.1 ^ (ceiling (x * 0.45))
    log = toFixed1 F.log
    sin = toFixed1 F.sin
    cos = toFixed1 F.cos
    tan = toFixed1 F.tan
    asin = toFixed1 F.asin
    acos = toFixed1 F.acos
    atan = toFixed1 F.atan
    sinh = toFixed1 F.sinh
    cosh = toFixed1 F.cosh
    tanh = toFixed1 F.tanh
    asinh = toFixed1 F.asinh
    acosh = toFixed1 F.acosh
    atanh = toFixed1 F.atanh

toFixed0 :: (Epsilon e) => (Rational -> Rational) -> Fixed e
toFixed0 f = r
    where r = F $ f $ getEps r

toFixed1 :: (Epsilon e) => (Rational -> Rational -> Rational) -> Fixed e -> Fixed e
toFixed1 f x@(F r) = F $ f (getEps x) r

instance (Epsilon e) => RealFloat (Fixed e) where
    exponent _ = 0
    scaleFloat 0 x = x
    isNaN _ = False
    isInfinite _ = False
    isDenormalized _ = False
    isNegativeZero _ = False
    isIEEE _ = False
    -- Explicitly undefine these rather than omitting them; this
    -- prevents a compiler warning at least.
    floatRadix = undefined
    floatDigits = undefined
    floatRange = undefined
    decodeFloat = undefined
    encodeFloat = undefined

-----------

-- The call @dynmicEps r f v@ evaluates @f v@ to a precsion of @r@.
dynamicEps :: forall a . Rational -> (forall e . Epsilon e => Fixed e -> a) -> Rational -> a
dynamicEps r f v = loop (undefined :: Eps1)
  where loop :: forall x . (Epsilon x) => x -> a
        loop e = if eps e <= r then f (fromRational v :: Fixed x) else loop (undefined :: EpsDiv10 x)

-- | The call @with_added_precision r f v@ evaluates @f v@, while
-- temporarily multiplying the precision of /v/ by /r/.
with_added_precision :: forall a f.(Epsilon f) => Rational -> (forall e.(Epsilon e) => Fixed e -> a) -> Fixed f -> a
with_added_precision r f v = dynamicEps (p*r) f (toRational v) where
  p = precision v