13

浮動小数点数を定義された桁数に丸める関数を作成しようとしています。私がこれまでに思いついたのはこれです:

import Numeric;

digs :: Integral x => x -> [x] <br>
digs 0 = [] <br>
digs x = digs (x `div` 10) ++ [x `mod` 10]

roundTo x t = let d = length $ digs $ round x <br>
                  roundToMachine x t = (fromInteger $ round $ x * 10^^t) * 10^^(-t)
              in roundToMachine x (t - d)

関数を使用しdigsてコンマの前の桁数を決定し、入力値を最適化しています (つまり、コンマを超えてすべてを移動すると、 に1.234なります0.1234 * 10^1)

関数はほとんどのroundTo入力で機能するようですが、一部の入力では奇妙な結果が得roundTo 1.0014 4られ1.0010000000000001ます1.001

この例の問題は、計算によって引き起こされます1001 * 1.0e-3(これは を返します1.0010000000000001) 。

これは、私が一緒に暮らさなければならないHaskellの数値表現の問題ですか、それとも浮動小数点数を特定の桁数に丸めるより良い方法はありますか?

4

3 に答える 3

24

この質問はほぼ2年前に投稿されたことに気づきましたが、文字列変換を必要としない回答を試してみたいと思いました.

-- x : number you want rounded, n : number of decimal places you want...
truncate' :: Double -> Int -> Double
truncate' x n = (fromIntegral (floor (x * t))) / t
    where t = 10^n

-- How to answer your problem...
λ truncate' 1.0014 3
1.001

-- 2 digits of a recurring decimal please...
λ truncate' (1/3) 2
0.33

-- How about 6 digits of pi?
λ truncate' pi 6
3.141592

私はそれを徹底的にテストしていないので、これが機能しない数字を見つけた場合はお知らせください!

于 2015-08-11T22:20:31.920 に答える
8

これは、浮動小数点の問題ほど Haskell の問題ではありません。各浮動小数点数は有限数のビットで実装されるため、完全に正確に表現できない数が存在します。これは、の代わりに0.1 + 0.2ぎこちなく返される を計算することによっても確認できます。これは、言語とハードウェア アーキテクチャで浮動小数点数がどのように実装されるかに関係しています。0.300000000000000040.3

解決策は、計算を行うために引き続きroundTo関数を使用することです (特別なライブラリがなくても得られるのと同じくらい正確です) が、画面に出力したい場合は、Text.Printf.printf関数などの文字列の書式設定を使用する必要があります。次のような文字列に変換するときに、丸める桁数を指定できます

import Text.Printf

roundToStr :: (PrintfArg a, Floating a) => Int -> a -> String
roundToStr n f = printf ("%0." ++ show n ++ "f") f

しかし、前述したように、これは数値ではなく文字列を返します。

編集:

より良い方法は

roundToStr :: (PrintfArg a, Floating a) => Int -> a -> String
roundToStr n f = printf (printf "%%0.%df" n) f

しかし、どちらが実際に速いかを確認するためにベンチマークを行っていません。ただし、どちらもまったく同じように機能します。

編集2:

@augustss が指摘しているように、

roundToStr :: (PrintfArg a, Floating a) => Int -> a -> String
roundToStr = printf "%0.*f"

これは、以前は知らなかったフォーマット規則を使用しています。

于 2013-09-10T16:00:38.743 に答える