在ghci终端中,我使用sqrt
函数计算了一些使用Haskell的方程式。
我注意到我的sqrt
结果有时会失去精确度,因为它应该被简化。
例如,
sqrt 4 * sqrt 4 = 4 -- This works well!
sqrt 2 * sqrt 2 = 2.0000000000000004 -- Not the exact result.
通常,我希望结果为2
。
有没有办法获得正确的简化结果?
这在Haskell中是如何工作的?
答案 0 :(得分:6)
Haskell中有可用的精确数字库。我想到的两个问题是cyclotomic和numbers包中的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提供的浮点数慢得多。对于许多计算而言,浮点引起的精度损失不是问题。