[haskell]Haskellで任意精度小数を扱う

Data.Fixedモジュールを用います。
単純に使うだけなら、モジュールをimportして具体的な精度をもつ型(Uni, Deci, Centi, Milli, Micro, Nano, Pico)で数値リテラルをアノテートなどして用います。
JavaBigDecimalに相当するものでしょうか?(Javaは良く知りませんが..)

import Data.Fixed (Pico)
main = print (1.2345678 + 3.00000012345 :: Pico) -- 1e-12精度での演算


Data.Fixedを見てみると、3つの関数(div', mod', divMod')と、
任意精度小数のための型コンストラクタFixed, 型クラスHasResolution, 表示用関数showFixed,
具体的な精度の型等exportされています。

module Data.Fixed
(
    div',mod',divMod',

    Fixed,HasResolution(..),
    showFixed,
    E0,Uni,
    E1,Deci,
    E2,Centi,
    E3,Milli,
    E6,Micro,
    E9,Nano,
    E12,Pico
) where


自分で具体的な精度を持つ型を作成することも出来ます。
ここでは小数点以下60桁の精度を持つ型(TooPrecise)を作ってみます。

import Data.Fixed

data E60 = E60
instance HasResolution E60 where
    resolution _ =
	1000000000000000000000000000000000000000000000000000000000000
type TooPrecise = Fixed E60

main = do
    print (3.0000000000000000000000000003 :: TooPrecise)
    print $ sum [0.1 :: TooPrecise, 0.2 .. 0.4]

何故任意精度の小数を誤差無しに扱えるのでしょうか。
(Fixed a)型の内部では具体的な数値と精度をIntegerとして別々に保持しているからです。
例えば(0.12::Fixed E3)は(120::Integer)と(1000::Integer)という2つの数値を持っている訳ですね。
なので計算自体はInteger同士で行うためコストは高く付きますが、精度内なら誤差なく計算が出来るということです。


単に型アノテートするだけで使えるのは、Haskellの数値リテラルが多相的だからですね。


型の相互変換にはtoRationalとfromRationalでしょうか。

changeResolution :: (HasResolution a, HasResolution b) => Fixed a -> Fixed b
changeResolution = fromRational . toRational

pico2nano :: Pico -> Nano
pico2nano = changeResolution

うーん有理数経由なので正確でしょうけど。