为何使用该Python求根代码时输出始终存在±0.0000001的偏差?
我编写了一个用于计算数字平方根的小型Python程序,代码如下:
def findRoot(number): a = 0 aa = 0.00 aaa = 0.0000 aaaa = 0.000000 aaaaa = 0.00000000 x = 1 while x == 1: if a * a > number: break else: a = a + 1 a = a - 1 aa = aa + a while x == 1: if aa * aa > number: break else: aa = aa + 0.01 aa = aa - 0.01 aaa = aaa + aa while x == 1: if aaa * aaa > number: break else: aaa = aaa + 0.0001 aaa = aaa - 0.0001 aaaa = aaaa + aaa while x == 1: if aaaa * aaaa > number: break else: aaaa = aaaa + 0.000001 # 后续省略的逻辑类似
但每次运行该代码时,输出结果总是存在0.0000001的偏差(偏高或偏低),请问这是什么原因?
问题原因分析
1. 浮点数的二进制精度限制
这是最核心的问题——计算机里的float类型是用二进制存储的,很多十进制小数根本没法被二进制精确表示。比如你用的0.01、0.0001这些步长,在二进制里都是无限循环的近似值。每次累加这些近似值时,误差会一点点累积,到最后就变成了你看到的0.0000001左右的偏差。
举个简单的例子:0.1在二进制里是无限循环的,你在Python里跑0.1 + 0.1 + 0.1,结果会是0.30000000000000004,这就是浮点数精度误差的直观体现,你的程序里多次累加步长,自然会把这种误差放大。
2. 循环终止逻辑的“过冲”问题
你的循环逻辑是:每次加步长,直到平方超过目标数,再退一步得到近似值。但因为浮点数的精度误差,有时候累加后的数值可能已经因为精度问题稍微“超标”,或者没达到预期精度就触发了终止条件,导致最终结果要么偏高一丢丢,要么偏低一丢丢。
比如假设目标数的平方根是2.0,但因为浮点数误差,当你累加到2.0000001时才触发aaa * aaa > number,减去步长后就得到1.9999999,偏差就这么来了。
改进建议
方法一:用牛顿迭代法替代逐位累加
逐位累加不仅效率低,还容易受精度问题影响。牛顿迭代法是计算平方根的高效且精准的方案,核心逻辑是通过不断迭代逼近真实值,公式很简单:x = (x + number / x) / 2。你可以设定一个精度阈值,当两次迭代的差值小于这个阈值时就停止,示例代码:
def findRoot(number, precision=1e-8): if number < 0: raise ValueError("负数没有实数平方根") x = number # 初始值 while True: next_x = (x + number / x) / 2 if abs(next_x - x) < precision: break x = next_x return x
这种方法的精度完全可控,而且受浮点数误差的影响极小。
方法二:调整循环的终止判断逻辑
如果你坚持想用逐位累加的思路,可以把终止条件从“平方超过目标数”改成“平方与目标数的差值小于你要的精度”,同时尽量减少累加次数。比如:
def findRoot(number): result = 0.0 # 找整数部分 while (result + 1)**2 <= number: result += 1 # 小数点后两位 step = 0.01 while (result + step)**2 <= number: result += step # 小数点后四位 step = 0.0001 while abs((result + step)**2 - number) > 1e-8: if (result + step)**2 <= number: result += step else: break return result
不过这种方式还是没法完全避免浮点数精度问题,只能稍微缓解偏差。
内容的提问来源于stack exchange,提问作者Djaro

