为什么math
模块返回错误的结果?
A = 12345678917
print 'A =',A
B = sqrt(A**2)
print 'B =',int(B)
结果
A = 12345678917
B = 12345678917
这里,结果是正确的。
A = 123456758365483459347856
print 'A =',A
B = sqrt(A**2)
print 'B =',int(B)
结果
A = 123456758365483459347856
B = 123456758365483467538432
此处结果不正确。
为什么会这样?
答案 0 :(得分:4)
因为math.sqrt(..)
首先将数字转换为浮点并且浮点数具有有限的 mantisse :它只能正确表示数字的一部分。因此float(A**2)
不等于A**2
。接下来,它会计算math.sqrt
,这也是近似正确的。
使用浮点的大多数函数永远不会完全正确对应它们的整数对应物。浮点计算几乎具有近似性。
如果计算A**2
,则得到:
>>> 12345678917**2
152415787921658292889L
现在,如果将其转换为float(..)
,则会获得:
>>> float(12345678917**2)
1.5241578792165828e+20
但如果你现在问两者是否相等:
>>> float(12345678917**2) == 12345678917**2
False
因此,在将其转换为浮动信息时,信息已丢失。
您可以在维基百科关于IEEE-754的文章中详细了解浮点数的工作原理以及这些浮点数的近似值,这是关于浮点数如何运作的正式定义。
答案 1 :(得分:4)
documentation for the math module状态"它提供对C标准定义的数学函数的访问。"它还声明"除非另有明确说明,否则所有返回值都是浮点数。"
这些一起意味着平方根函数的参数是浮点值。在大多数系统中,这意味着一个适合8个字节的浮点值,称为" double"在C语言中。您的代码在计算平方根之前将整数值转换为这样的值,然后返回这样的值。
但是,8字节浮点值可以存储at most 15 to 17 significant decimal digits。这就是你在结果中得到的结果。
如果要在平方根中获得更好的精度,请使用保证为整数参数提供完全精度的函数。只需进行网络搜索,您就会找到几个。那些人通常会使用Newton-Raphson方法的变体来迭代并最终以正确的答案结束。请注意,这比数学模块的sqrt函数慢得多。
这是我从互联网上修改过的例程。我现在无法引用消息来源。此版本也适用于非整数参数,但只返回平方根的整数部分。
def isqrt(x):
"""Return the integer part of the square root of x, even for very
large values."""
if x < 0:
raise ValueError('square root not defined for negative numbers')
n = int(x)
if n == 0:
return 0
a, b = divmod(n.bit_length(), 2)
x = (1 << (a+b)) - 1
while True:
y = (x + n//x) // 2
if y >= x:
return x
x = y
答案 2 :(得分:2)
如果您想计算真正大数字的sqrt并且需要精确结果,可以使用sympy
:
import sympy
num = sympy.Integer(123456758365483459347856)
print(int(num) == int(sympy.sqrt(num**2)))
答案 3 :(得分:0)
浮点数存储在内存中的方式使得它们的计算容易出现轻微错误,但在需要精确结果时仍然很重要。正如其中一条评论中所述,?
库可以在这里为您提供帮助:
decimal
我使用版本3.6,它对整数的大小没有硬编码限制。我不知道,在2.7中,将>>> A = Decimal(12345678917)
>>> A
Decimal('123456758365483459347856')
>>> B = A.sqrt()**2
>>> B
Decimal('123456758365483459347856.0000')
>>> A == B
True
>>> int(B)
123456758365483459347856
视为B
会导致溢出,但int
无论如何都非常有用。