我正在尝试创建一个函数来将浮点数舍入到定义的数字长度。到目前为止我得出的是:
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的数字表示中的一个问题,我不得不忍受或者是否有更好的方法将浮点数舍入到特定的数字长度?
答案 0 :(得分:13)
我意识到这个问题已经发布了差不多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
我没有对它进行过彻底的测试,所以如果你找到了数字,这对我来说是行不通的!
答案 1 :(得分:6)
这不像浮点问题那样是一个haskell问题。由于每个浮点数以有限数量的位实现,因此存在不能完全准确表示的数字。您还可以通过计算0.1 + 0.2
来查看此内容,该0.30000000000000004
笨拙地返回0.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
但正如我所提到的,这将返回一个字符串而不是一个数字。
编辑:
更好的方法可能是
roundToStr :: (PrintfArg a, Floating a) => Int -> a -> String
roundToStr = printf "%0.*f"
但我没有基准测试看哪个实际上更快。两者的工作原理完全相同。
编辑2:
正如@augustss指出的那样,只需
即可轻松完成{{1}}
使用我以前不知道的格式规则。
答案 2 :(得分:0)