对ghci简单算术的怀疑

时间:2018-10-13 15:49:05

标签: haskell floating-point decimal rounding

我开始使用Miran Lipovaca's famous book学习Haskell,但出于好奇,我在与格拉斯哥Haskell编译器的交互式shell(ghci)进行的第一次交互中就停了下来。

特别是,我首先将两个整数相除以获得浮点十进制数,这基本上是为了了解Haskell如何自动管理它们并了解有关其自动转换的更多信息。

λ> 1/3
0.3333333333333333
λ> 4/3
1.3333333333333333
λ> 3424/3
1141.3333333333333

这些告诉我,Haskell总共使用17位数字(或包括点的18个字符?),无论它们是否有效。但是,这些也发生了

λ> 14/3
4.666666666666667
λ> 34/3
11.333333333333334
λ> 44/3
14.666666666666666

为什么第一个短一位?为什么其他人错误地取整?

可能这是一个愚蠢的问题,但是我感觉要知道这些基本问题的答案,我可以通过更深入地了解语言(或解释器)的工作方式来加深对语言(或解释器?)的理解。 / p>

1 个答案:

答案 0 :(得分:10)

Haskell specification在浮点格式和行为的规范上有些懈怠。它说Haskell的Double类型应在范围和精度上涵盖IEEE“双精度”,Haskell Prelude定义的默认操作不符合某些标准,但是IEEE浮点的某些方面已在Prelude类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

现在,我们将从以下几个令人惊讶的情况开始:

λ> 14/3
4.666666666666667

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

我在Haskell规范中看不到有关浮点数在显示时应如何格式化或以其他方式转换为十进制的规则。一个解释这一点的规则是Java和其他一些软件采用的规则:产生足够的十进制数字,将十进制数字转换回浮点格式,从而产生原始数字。即,仅产生足够的数字来唯一地标识内部值。 (其他不常见的规则是转换为固定的位数,或转换为固定的位数,然后删除尾随的零。正当规则和remoe-trailing-zeroes规则都将产生如图所示的结果问题。我将在此答案中证明正当规则。

14/3产生的值正好是 4.66666666666666696272613990004174411296844482421875。连同下一个较低和下一个较大的可表示值,我们得到了(在第16个 数字后插入一个空格,以帮助可视化):

4.666666666666666 0745477201999165117740631103515625
4.666666666666666 96272613990004174411296844482421875
4.666666666666667 850904559600166976451873779296875

如果我们将4.666666666666667转换为浮点数,那么应该得出以下哪个值?中间一个更近。它仅相差0.04(以最低位数表示),而其他相距相差0.93和.15。因此,16位数字“ 4.666666666666667”足以唯一地标识4.66666666666666696272613990004174411296844482421875。

相反,请考虑4/3,即1.3333333333333333332593184650249895639717578887939453125。它和它的两个邻居是:

1.333333333333333 03727386009995825588703155517578125
1.333333333333333 2593184650249895639717578887939453125
1.333333333333333 481363069950020872056484222412109375

同样,空格位于第16个 位之后。如果我们将16位数字1.333333333333333转换为浮点数,应该是哪个结果?现在第一个更近了;只有0.04个单位在外。因此,“ 1.333333333333333”无法代表正确的内部值。我们需要17位数字“ 1.3333333333333333”来唯一标识所需的值。

下一个情况是:

λ> 34/3
11.333333333333334

问题询问为什么将其“错误地取整”。事实并非如此。内部值为11.3333333333333339254522798000834882259368896484375。此数字及其两个相邻的可代表值是:

11.333333333333332149095440399833023548126220703125
11.3333333333333339254522798000834882259368896484375
11.33333333333333570180911920033395290374755859375

中间的那个最接近11⅓,所以它是34/3的正确结果。并且“ 11.333333333333334”是将11.3333333333333339259254522798000834882259368896484375正确转换为17位十进制数字。

类似地,在:

λ> 44/3
14.666666666666666

候选结果为:

14.666666666666664 29819088079966604709625244140625
14.666666666666666 0745477201999165117740631103515625
14.666666666666667 850904559600166976451873779296875

它们的中间距离14⅔更近,因为它们相距约.59单位(使用在我标记有空格的位置处的单位),而最后一个相距1.18单位。因此正确的内部结果是14.6666666666666660745477201999165117740631103515625,并将其转换为17个十进制数字的结果是14.666666666666666。