怀疑ghci简单的算术

Jef*_*ski 0 floating-point haskell decimal rounding

我开始使用Miran Lipovaca着名的书来学习Haskell ,但是在与格拉斯哥Haskell编译器的交互式shell(ghci)的第一次交互中,我的好奇心阻止了我.

特别是,我开始通过划分两个整数来获得浮点十进制数,基本上看看Haskell如何自动管理它们并了解更多关于它的自动转换.

?> 1/3
0.3333333333333333
?> 4/3
1.3333333333333333
?> 3424/3
1141.3333333333333
Run Code Online (Sandbox Code Playgroud)

这些告诉我Haskell总共使用了17位数字(或18个字符,包括点?),无论它们是否重要.然而,这些也发生了

?> 14/3
4.666666666666667
?> 34/3
11.333333333333334
?> 44/3
14.666666666666666
Run Code Online (Sandbox Code Playgroud)

为什么第一个缩短一位数?为什么其他人错误地四舍五入?

可能这是一个愚蠢的问题,但我想知道这些基本事情的答案,我可以从更深入地理解语言(或翻译?)的工作方式开始,通过了解更多内容.

Eri*_*hil 10

哈斯克尔规范的叶子在其浮点格式和行为规范有些呆滞.它说Haskell的Double类型应该在范围和精度上涵盖IEEE"双精度",Haskell Prelude定义的默认操作不符合某些标准,但是在Prelude类中已经考虑了IEEE浮点的某些方面RealFloat.对于这个答案,我将演示IEEE-754基本64位二进制浮点格式和算法中问题的结果.

问题是:

这些告诉我Haskell总共使用了17位数字(或18个字符,包括点?),无论它们是否重要.

这是不正确的.如本回答所假设的那样,OP的Haskell实现可能就是这种情况,该数字有53个二进制数字,而不是17个十进制数字.可以显示17位数字,但这是转换显示数字的结果,而不是用于计算的实际值的精确表示.

显示的前三个案例并不显着,但为了说明,我们显示了内部值:

?> 1/3
0.3333333333333333 -- 0.333333333333333314829616256247390992939472198486328125
?> 4/3
1.3333333333333333 -- 1.3333333333333332593184650249895639717578887939453125
?> 3424/3
1141.3333333333333 -- 1141.333333333333257542108185589313507080078125
Run Code Online (Sandbox Code Playgroud)

现在我们来看看令人惊讶的案例,从以下开始:

?> 14/3
4.666666666666667
Run Code Online (Sandbox Code Playgroud)

令人惊讶的是,它以16位十进制数字显示,而之前的结果显示为17.为什么?

我没有看到Haskell规范中有关如何在显示或以其他方式转换为十进制时应格式化浮点数的规则.解释这一点的一条规则是Java和其他一些软件采用的规则:产生足够的十进制数字,将十进制数字转换回浮点格式产生原始数字.也就是说,只产生足够的数字来唯一地识别内部值.(其他不常见的规则是转换为固定数字的数字,或转换为固定数字的数字,然后删除尾随零.正则足够的规则和remoe-trailing-zeroes规则将产生显示的结果问题.我将在这个答案中展示恰到好处的规则.)

产生的值14/3恰好是4.66666666666666696272613990004174411296844482421875.连同下一个较低的下一个和更大的可表示的值,我们有(与16之后插入的空间位,以协助可视化):

4.666666666666666 0745477201999165117740631103515625
4.666666666666666 96272613990004174411296844482421875
4.666666666666667 850904559600166976451873779296875
Run Code Online (Sandbox Code Playgroud)

如果我们将4.666666666666667转换为浮点数,那么上述哪个值应该是结果?中间的更接近; 它只有大约0.04的距离(以最低位数为单位),而其他距离是.93和.15.因此,16位"4.666666666666667"足以唯一地识别4.66666666666666696272613990004174411296844482421875.

相反,考虑4/3,即1.3333333333333332593184650249895639717578887939453125.它和它的两个邻居是:

1.333333333333333 03727386009995825588703155517578125
1.333333333333333 2593184650249895639717578887939453125
1.333333333333333 481363069950020872056484222412109375
Run Code Online (Sandbox Code Playgroud)

再次,空间16后一位数字.如果我们将16位数1.333333333333333转换为浮点数,那么结果应该是哪一个?现在第一个更接近; 它只有0.04个单位.因此"1.333333333333333"无法表示正确的内部值.我们需要17位"1.3333333333333333"以便唯一地识别所需的值.

下一个案例是:

?> 34/3
11.333333333333334
Run Code Online (Sandbox Code Playgroud)

问题是为什么这是"错误的四舍五入".事实并非如此.内部值为11.3333333333333339254522798000834882259368896484375.此数字及其两个相邻的可表示值为:

11.333333333333332149095440399833023548126220703125
11.3333333333333339254522798000834882259368896484375
11.33333333333333570180911920033395290374755859375
Run Code Online (Sandbox Code Playgroud)

中间的一个最接近11⅓,所以它是正确的结果34/3.并且"11.333333333333334"是11.3333333333333339254522798000834882259368896484375到17位十进制数的正确转换.

同样,在:

?> 44/3
14.666666666666666
Run Code Online (Sandbox Code Playgroud)

候选人的结果是:

14.666666666666664 29819088079966604709625244140625
14.666666666666666 0745477201999165117740631103515625
14.666666666666667 850904559600166976451873779296875
Run Code Online (Sandbox Code Playgroud)

它们的中间距离接近14⅔,因为它距离大约.59个单位(在我用空格标记的位置使用单位),而最后一个距离是1.18个单位.所以正确的内部结果是14.6666666666666660745477201999165117740631103515625,将其转换为17位小数的​​结果是14.666666666666666.