【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 辅助进行内容的书写和完善

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/71812.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI无代码平台

以下是目前支持快速开发产品的高生产力免费AI无代码平台推荐&#xff0c;按功能和适用场景分类&#xff1a; 一、全栈应用开发类 Bolt.DIY DeepSeek-R1 无需编写代码即可开发全栈应用&#xff0c;提供免费API和无速率限制&#xff0c;支持AI编码助手与自动化流程 。 优势&…

Gini系数的应用 - 指标波动贡献分析

基尼系数的定义 基尼系数是衡量数据分布不均衡程度的指标&#xff0c;取值范围在0到1之间&#xff1a; 0 表示完全均衡&#xff08;所有值相等&#xff09;。1 表示完全不均衡&#xff08;所有值集中在一个点&#xff09;。 基尼系数的计算公式 假设有 n n n 个数据点&…

第一节: 网络基础与参考模型

深入理解OSI七层模型与TCP/IP四层模型:网络工程师的入门指南 在网络通信的世界中,OSI七层模型和TCP/IP四层模型是理解网络架构的基础。无论是配置路由器、排查网络故障,还是设计复杂的网络系统,掌握这些模型的分层结构及其功能都是必不可少的。本文将从新手角度出发,深入…

HTTP拾技杂谈

HTTP拾技杂谈 简单聊聊HTTP中的那些东西 文章目录 HTTP拾技杂谈前言HTTP协议1.请求从客户端到服务器端的4个步骤一般客户端请求如下&#xff1a;服务端响应如下 2.Keep-AliveHTTP方法Cookie 总结 前言 超文本传输协议&#xff08;Hypertext Transfer Protocol &#xff0c;HT…

用Deepseek写一个五子棋微信小程序

在当今快节奏的生活中&#xff0c;休闲小游戏成为了许多人放松心情的好选择。五子棋作为一款经典的策略游戏&#xff0c;不仅规则简单&#xff0c;还能锻炼思维。最近&#xff0c;我借助 DeepSeek 的帮助&#xff0c;开发了一款五子棋微信小程序。在这篇文章中&#xff0c;我将…

自然语言处理:最大期望值算法

介绍 大家好&#xff0c;博主又来给大家分享知识了&#xff0c;今天给大家分享的内容是自然语言处理中的最大期望值算法。那么什么是最大期望值算法呢&#xff1f; 最大期望值算法&#xff0c;英文简称为EM算法&#xff0c;它的核心思想非常巧妙。它把求解模型参数的过程分成…

【从零开始学习计算机科学】计算机体系结构(一)计算机体系结构、指令、指令集(ISA)与量化评估

【从零开始学习计算机科学】计算机体系结构(一)计算机体系结构、指令、指令集(ISA)与量化评估 概论计算机体系结构简介计算机的分类并行体系结构指令集体系结构(ISA)分类存储器寻址寻址模式操作数大小指令ISA的编码程序的优化计算机体系结构量化评估存储器体系结构概论 …

Electron使用WebAssembly实现CRC-32 常用标准校验

Electron使用WebAssembly实现CRC-32 常用标准校验 将C/C语言代码&#xff0c;经由WebAssembly编译为库函数&#xff0c;可以在JS语言环境进行调用。这里介绍在Electron工具环境使用WebAssembly调用CRC-32 常用标准格式校验的方式。 CRC-32 常用标准校验函数WebAssembly源文件…

Docker基础篇——Ubuntu下Docker安装

大家好我是木木&#xff0c;在当今快速发展的云计算与云原生时代&#xff0c;容器化技术蓬勃兴起&#xff0c;Docker 作为实现容器化的主流工具之一&#xff0c;为开发者和运维人员带来了极大的便捷 。下面我们一起进行Docker安装。 Docker的官方Ubuntu安装文档&#xff0c;如…

第五课:Express框架与RESTful API设计:技术实践与探索

在使用Node.js进行企业应用开发&#xff0c;常用的开发框架Express&#xff0c;其中的中间件、路由配置与参数解析、RESTful API核心技术尤为重要&#xff0c;本文将深入探讨它们在应用开发中的具体使用方法&#xff0c;最后通过Postman来对开发的接口进行测试。 一、Express中…

mitmproxy配合Wireshark 抓包分析

Mitmproxy 是一款非常强大的 交互式 HTTP 代理 工具&#xff0c;它被广泛应用于 Web 开发、API 调试、安全测试 等领域。与 Wireshark 侧重于被动监听网络流量不同&#xff0c;Mitmproxy 更像一个 主动的中间人&#xff0c;可以拦截、检查、修改和重放 HTTP/HTTPS 流量&#xf…

Varlens(手机上的单反)Ver.1.9.3 高级版.apk

Varlens 是一款专业级手机摄影软件&#xff0c;旨在通过丰富的功能和高自由度参数调节&#xff0c;让手机拍摄效果媲美微单相机。以下是核心功能总结&#xff1a; 一、核心功能 专业拍摄模式 支持手动/自动/程序模式&#xff0c;可调节ISO、快门速度、EV、白平衡等参数27 提供…

Scala 中的访问修饰符

在Scala中&#xff0c;面向对象的权限控制主要通过访问修饰符来实现。Scala提供了以下几种访问修饰符来控制类、对象、成员变量和方法的访问权限&#xff1a; 1. 默认访问权限&#xff08;无修饰符&#xff09; 如果没有指定任何访问修饰符&#xff0c;成员默认是public的&…

第十五届蓝桥杯省赛电子类单片机学习过程记录(客观题)

客观试题: 01.典型的BUCK电源电路包含哪些关键器件(ABCD) A. 电容 B. 二极管 C. 电感 D. MOSFET 解析: 典型的 BUCK 电源电路是一种降压型的直流-直流转换电路,它包含以下关键器件: A.电容:电容在电路中起到滤波的作用。输入电容用于平滑输入电压的波动,减少电源噪声对…

Dify使用日常:我是如何按标题级别将word中的内容转存到excel中的

先上效果图 word中的内容 转存到excel之后 实现步骤&#xff1a; 1、在dify中创建一个工作流&#xff0c;如上图 2、在开始节点增加一个支持文件上传的变量 3、添加文档提取器&#xff0c;提取上传的文件中的内容 4、添加大模型节点&#xff0c;将文档提取器提取出来的内容&…

Vue 框架深度解析:源码分析与实现原理详解

文章目录 一、Vue 核心架构设计1.1 整体架构流程图1.2 模块职责划分 二、响应式系统源码解析2.1 核心类关系图2.2 核心源码分析2.2.1 数据劫持实现2.2.2 依赖收集过程 三、虚拟DOM与Diff算法实现3.1 Diff算法流程图3.2 核心Diff源码 四、模板编译全流程剖析4.1 编译流程图4.2 编…

IDEA与Maven使用-学习记录(持续补充...)

1. 下载与安装 以ideaIU-2021.3.1为例&#xff0c;安装步骤&#xff1a; 以管理员身份启动ideaIU-2021.3.1修改安装路径为&#xff1a;D:\Program Files\JetBrains\IntelliJ IDEA 2021.3.1勾选【创建桌面快捷方式】&#xff08;可选&#xff09;、【打开文件夹作为项目】&…

认识vue2脚手架

1.认识脚手架结构 使用VSCode将vue项目打开&#xff1a; package.json&#xff1a;包的说明书&#xff08;包的名字&#xff0c;包的版本&#xff0c;依赖哪些库&#xff09;。该文件里有webpack的短命令&#xff1a; serve&#xff08;启动内置服务器&#xff09; build命令…

SQL经典查询

查询不在表里的数据&#xff0c;一张学生表&#xff0c;一张学生的选课表&#xff0c;要求查出没有选课的学生&#xff1f; select students.student_name from students left join course_selection on students.student_idcourse_selection.student_id where course_selecti…

《机器学习数学基础》补充资料:过渡矩阵和坐标变换推导

尽管《机器学习数学基础》这本书&#xff0c;耗费了比较长的时间和精力&#xff0c;怎奈学识有限&#xff0c;错误难免。因此&#xff0c;除了在专门的网页&#xff08; 勘误和修订 &#xff09;中发布勘误和修订内容之外&#xff0c;对于重大错误&#xff0c;我还会以专题的形…