欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > 【Python】整数除法不正确,少1的问题,以及有关浮点数转换的精度问题

【Python】整数除法不正确,少1的问题,以及有关浮点数转换的精度问题

2025/3/11 7:50:10 来源:https://blog.csdn.net/qq_35353673/article/details/146166431  浏览:    关键词:【Python】整数除法不正确,少1的问题,以及有关浮点数转换的精度问题

1. 问题

今天在做leetcode 不同路径 的时候发现了个问题

对于m=53 n=4class Solution:def uniquePaths(self, m: int, n: int) -> int:rlt = 1for i in range(0, m-1):rlt *= (m + n - 2 - i)for i in range(0, m-1):rlt /= (i + 1)return int(rlt)为什么这个结果是 26234class Solution:def uniquePaths(self, m: int, n: int) -> int:up = 1down = 1for i in range(0, m-1):up *= (m + n - 2 - i)for i in range(0, m-1):down *= (i + 1)return int(up/down)而这个结果是 26235

据GPT描述,两个代码逻辑上是相同的,计算的是组合数 C m + n − 2 m − 1 C_{m+n-2}^{m-1} Cm+n2m1

C m + n − 2 m − 1 = ( m + n − 2 ) ! ( m − 1 ) ! ( n − 1 ) ! C_{m+n-2}^{m-1} = \frac{(m+n-2)!}{(m-1)!(n-1)!} Cm+n2m1=(m1)!(n1)!(m+n2)!

然而,两个代码的计算方式有所不同:

  1. 第一个代码 直接使用浮点除法 rlt /= (i + 1),在 Python 中,浮点数的计算可能会引入精度误差,最终 int(rlt) 向下取整,导致结果偏小。

  2. 第二个代码 分别计算 updown,然后用 int(up/down),由于 Python 的整数运算是精确的,这种方式避免了浮点误差,得到了正确的组合数 26235。

第一个代码因为浮点误差,导致 int(rlt) 取整后比正确值少 1,所以结果是 26234,而第二个代码由于整数计算的精确性,得到了正确答案 26235

2. 思考

即使除法的数学结果是整数,如果中间计算涉及浮点数,可能会因为浮点精度误差导致最终取整时丢失 1。我们来深入分析原因:

① Python 的 / 生成浮点数

Python 里的 / 即使除得的是整数,结果仍然是 float。例如:

print(10 / 2)  # 5.0
print(type(10 / 2))  # <class 'float'>

在代码中,这意味着:

rlt = int(rlt / (i + 1))

即使 rlt / (i + 1) 的数学结果是整数,Python 仍然会先计算 浮点数,然后 int() 强制取整,可能引入误差。


② 浮点数存储精度问题

Python 的浮点数是基于 IEEE 754 标准的 双精度浮点数(double precision)。但 某些整数在浮点数表示中无法精确存储,例如:

x = 10000000000000003
y = 10000000000000002print(float(x))  # 10000000000000004.0  (存储时发生变化)
print(float(y))  # 10000000000000002.0  (存储正确)

rlt 变大时,比如 C_{m+n-2}^{m-1} 计算过程中,rlt 可能会进入 浮点存储的误差范围,导致浮点数计算不精确。


int() 取整丢失 1

浮点数不能精确表示某些大整数时,取 int() 可能会导致向下取整。假设 rlt / (i + 1) 的数学结果是 26235,但由于浮点精度问题,Python 存储的可能是:

26234.999999999998

此时 int(26234.999999999998) 向下取整,结果变成 26234,比正确答案少了 1。


④ 解决方案

避免浮点数参与计算,直接使用 整数除法 //,确保所有计算都在 整数空间 进行:

rlt //= (i + 1)  # 这样不会有浮点精度问题

这样,每次除法都保证是 整数运算,不会因浮点精度误差导致 int() 取整出错。

3. 进一步理解

为什么x = 10000000000000003
y = 10000000000000002print(float(x))  # 10000000000000004.0  (存储时发生变化)
print(float(y))  # 10000000000000002.0  (存储正确)

这个现象是由于 IEEE 754 双精度浮点数(double precision floating point)有限精度 导致的。让我们来深入分析为什么 10000000000000003 在转换成 float 时变成了 10000000000000004.0,而 10000000000000002 仍然能正确存储。

① 浮点数的存储方式

Python 的 float 类型遵循 IEEE 754 双精度浮点数标准,使用 64 位 进行存储:

  • 1 位 表示符号位(正/负)。
  • 11 位 表示指数(决定浮点数的范围)。
  • 52 位 表示 尾数(精确存储的部分)。

由于 52 位尾数最多只能精确存储 52 位二进制数,所以如果一个整数的二进制表示超过 52 位,就必须 进行四舍五入,这可能导致数值发生变化。


1000000000000000310000000000000002 的二进制表示

我们先看看这两个数的 二进制表示(十进制 → 二进制):

10000000000000002
10000000000000002 = 10001110000110111100100110101100111011001000000000000000000010 (二进制)
  • 这个数的尾数(低 52 位)可以被精确存储,所以转换成 float 时不会损失精度。
10000000000000003
10000000000000003 = 10001110000110111100100110101100111011001000000000000000000011 (二进制)
  • 这个数的尾数超过了 52 位的存储限制,所以 IEEE 754 必须进行四舍五入
  • 由于 IEEE 754 采用 “最接近偶数舍入”(round to nearest, ties to even),它会 向上舍入10000000000000004,因此 float(10000000000000003) 变成了 10000000000000004.0

③ 为什么 10000000000000003 变成 10000000000000004.0

当一个整数大到超出 52 位精度范围 时:

  1. 无法精确存储,需要进行近似表示。
  2. IEEE 754 采用“四舍五入到最近的偶数”规则
    • 1000000000000000310000000000000004 更接近 10000000000000004,所以它会被 向上舍入
    • 如果它的二进制表示是 ...00011(奇数结尾),IEEE 754 会向上舍入到 ...00100(偶数)。

所以:

print(float(10000000000000003))  # 10000000000000004.0
print(float(10000000000000002))  # 10000000000000002.0

这个现象就是 浮点数的精度限制 造成的,它在转换时 自动进行了四舍五入,使得 10000000000000003 变成了 10000000000000004.0

总结

  • Python float 遵循 IEEE 754 双精度浮点数(64 位),其中只有 52 位 用于存储尾数,超过部分会 四舍五入
  • 10000000000000002 可以精确存储,因为它的二进制表示没有超出 52 位精度范围。
  • 10000000000000003 由于超出 52 位精度范围,被四舍五入成 10000000000000004.0

写在最后

本文采用了 ChatGPT 辅助进行内容的书写和完善

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词