精度损失'sqrt'Haskell

时间:2014-06-28 12:50:18

标签: haskell

在ghci终端中,我使用sqrt函数计算了一些使用Haskell的方程式。

我注意到我的sqrt结果有时会失去精确度,因为它应该被简化。

例如,

sqrt 4 * sqrt 4 = 4 -- This works well!
sqrt 2 * sqrt 2 = 2.0000000000000004 -- Not the exact result.

通常,我希望结果为2

有没有办法获得正确的简化结果?
这在Haskell中是如何工作的?

3 个答案:

答案 0 :(得分:6)

Haskell中有可用的精确数字库。我想到的两个问题是cyclotomicnumbers包中的CReal模块。 (Cyclotomic数字不支持您可能喜欢的复数上的所有操作,但整数和有理数的平方根都在域中。)

>>> import Data.Complex.Cyclotomic 
>>> sqrtInteger 2
e(8) - e(8)^3
>>> toReal $ sqrtInteger 2 
Just 1.414213562373095     -- Maybe Double
>>> sqrtInteger 2 * sqrtInteger 2
2
>>> toReal $ sqrtInteger 2 * sqrtInteger 2
Just 2.0
>>> rootsQuadEq 3 2 1
Just (-1/3 + 1/3*e(8) + 1/3*e(8)^3,-1/3 - 1/3*e(8) - 1/3*e(8)^3)
>>> let eq x  = 3*x*x + 2*x + 1
>>> eq (-1/3 + 1/3*e(8) + 1/3*e(8)^3)
0
>>> import Data.Number.CReal 
>>> sqrt 2 :: CReal
1.4142135623730950488016887242096980785697 -- Show instance cuts off at 40th place
>>> sqrt 2 * sqrt 2 :: CReal
2.0
>>> sin 3  :: CReal
0.1411200080598672221007448028081102798469
>>> sin 3*sin 3 + cos 3*cos 3 :: CReal
1.0

答案 1 :(得分:2)

你不会失去精确度。你的精确度有限。

2的平方根是实数但不是有理数,因此它的值不能由任何计算机精确表示(当然,除了象征性地表示它)。

即使您定义了一个非常大的精度类型,它也无法准确表示2的平方根。您可能会获得更高的精度,但绝不足以完全表示该值(除非您拥有一台具有无限内存的计算机,在这种情况下请雇用我)。

答案 2 :(得分:0)

这些结果的解释取决于sqrt函数返回的值的类型:

> :t sqrt
sqrt :: Floating a => a -> a

Floating a表示返回的值属于Floating类型。 属于此类的所有类型的值都存储为浮点数。这些牺牲精度是为了覆盖更大范围的数字。

双精度浮点数可以覆盖非常大的范围,但它们的精度有限,无法编码所有可能的数字。平方根2(√2)就是这样一个数字:

> sqrt 2
1.4142135623730951
> sqrt 2 + 0.000000000000000001
1.4142135623730951

如上所述,双精度浮点数不可能足够精确地表示√2+ 0.000000000000000001,它只是四舍五入到可以用浮点编码表示的最接近的近似值。

正如另一张海报所提到的,√2是一个无理数,可以简化为需要无限数量的数字才能正确表示。因此,不能使用浮点数忠实地表示它。这会导致错误,例如在将其与自身相乘时发现的错误。

您可以在维基百科页面上了解浮点数:http://en.wikipedia.org/wiki/Floating_point

我特别建议您阅读其他Stack Overflow问题的答案:Floating Point Limitations并按照上述链接,它将帮助您了解幕后发生的事情。

请注意,这是每种语言的问题,而不仅仅是Haskell。完全摆脱它的一种方法是使用符号计算库,但它们比CPU提供的浮点数慢得多。对于许多计算而言,浮点引起的精度损失不是问题。