我尝试在Haskell中将一个双精度浮点数(Double
)连续除以2.0
一百次。
当我得到5960464477539.06250
的值时,用2.00000
除这个值得到2.9802322387695313e12
(或2980232238769.53130
),从数学上讲,正确的结果应该是2980232238769.53125
,但这里有一些精度损失。
import Text.Printf (printf)
import System.Exit (exitSuccess)
main = do
n <- readLn :: IO Double
reading n 0
reading n c = do
if c == 100
then exitSuccess
else printf "%.5f\n" n
reading (n/2.0) (c+1)
将数值直接除以ghci
:
Prelude> 5960464477539.06250/2
2.9802322387695313e12
我已经读过关于Data.Scientific
的文章,但是我没有访问这个模块的权限。如果没有它,在这种情况下有什么方法可以提高准确性吗?
2条答案
按热度按时间ndasle7k1#
实际上,你的数字已经被精确地计算出来了;只是在打印
Double
时,标准的做法是只打印唯一标识Double
所需的数字,在本例中,除以2后的数字比所有数字都少。如果愿意,可以编写自己的例程,将Double
转换为包含所有数字的精确十进制表示;我们使用了通常的除法+模约简技巧,用于显示正常整数的数字,但是当比率低于1时,需要一些额外的逻辑来插入小数点。但是......我一直想编写一个例程,将任意精度的
Rational
转换为任意基数的数字序列+序列循环位置的信息,所以我把它作为一个借口,终于做到了。这也可以用来再次检查我上面所说的关于在你的起始代码中可以使用全精度的事实。下面是我们的数据类型。我们的最终结果是:我们的意图是用
d
表示数字序列intro d ++ cycle (loop d)
(加上一些方便的辅助信息),下面是计算方法:有很多种方法可以将它打印到
String
中,下面是其中一种:我们可以在ghci里试试:
保留了全部精度。我们现在还可以通过在
Double
中执行所有计算,然后在打印之前转换为Rational
来验证答案的第一段:即使对于基于
Double
的计算,也会保留完全精度。0sgqnhkj2#
我只是用我想要的准确性解决了我的问题(感谢丹尼尔·瓦格纳)。
我使用了
Pico
类型(Fixed E12
),分辨率为10^-12 = .000000000001,来自Data.Fixed
模块。虽然它不准确,但对于我需要的准确性来说是合理的,而且很容易格式化。