【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+n?2m?1?

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+n?2m?1?=(m?1)!(n?1)!(m+n?2)!?

然而,兩個代碼的計算方式有所不同:

  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.pswp.cn/web/71812.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/71812.shtml
英文地址,請注明出處:http://en.pswp.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;我還會以專題的形…