[haskell]Haskellで任意精度小数を扱う
Data.Fixedモジュールを用います。
単純に使うだけなら、モジュールをimportして具体的な精度をもつ型(Uni, Deci, Centi, Milli, Micro, Nano, Pico)で数値リテラルをアノテートなどして用います。
JavaのBigDecimalに相当するものでしょうか?(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
うーん有理数経由なので正確でしょうけど。