Python3 里 7/3 的值為什麼會是 2.3333333333333335,末位為什麼會是 5?


python里7/3的值為什麼會是2.3333333333333335,末位為什麼會是5?

短回答:

因為浮點數的精度問題。

長回答:

7/3 結果是一個無限循環小數,而對於計算機,無限循環小數是無法保存的,要進行截斷處理。

你可能會問四捨五入的話應該是2.3333333333333333才對啊,結尾怎麼會是5呢?

其實,這裡的截斷處理是指在計算機浮點數表示法里進行截斷,以64位浮點數為例,有1位表示正負,11位表示指數,52位表示尾數。

計算機所做的事情,是計算 7/3 的值,直到52位尾數全部佔滿位以後停止,也就是截斷。

7/3 計算後為10.010101010101010101010101010101010101010101010101011,轉化為十進位後就是2.3333333333333335,末尾出現5就不奇怪了。

PS:

在python3中,7/3的計算結果以浮點數來表示,為2.3333333333333335。

在python2中,7/3的計算結果以整數來表示,為2.

最後給題主加把勁兒!


回復細心的 @張博宇 提到的問題:

一、10.01010....011,沒數錯的話似乎是53位數字(反正肯定是奇數位),而不是偶數位?

二、為什麼截斷的末尾是01011,而不是01010?

三、1.0/3,結果是0.33333...33,最後一位是3,是不是因為Python的二進位最後截斷是0.01010101...0101?

首先指出一處小錯誤:一中不是53位,是51位。

開始答題:

我們來親手算一下。7/3,在這裡我們暫時把二進位小數點算到53位以更清晰地說明問題:

10.01010101010101010101010101010101010101010101010101010

好戲要開始了,我們把上面這個能看花眼的二進位小數轉化為科學記數法:

(1.001010101010101010101010101010101010101010101010101010)_{2} *2^1

注意,由於左移了一位小數點,這時候小數點後有54位。

在64位浮點數中:

  • 1位符號為正,表示為『0』;
  • 11位指數(小數點位移量)為1,表示為『10000000000』;
  • 52位尾數為『0010101010101010101010101010101010101010101010101011『

    為什麼:尾部多出兩位』10『無法保留,所以0舍1入,在第52進一位,所以第52位的0變為1.

合起來,變為:

0100000000000010101010101010101010101010101010101010101010101011

這也是計算機在內存中存儲的方法。

那麼讀取的時候就要把它再拆過來:

  • 在尾數0010101010101010101010101010101010101010101010101011前面加上『1.』: 1.0010101010101010101010101010101010101010101010101011
  • 指數為10000000000,即1,小數點右移1位: 10.010101010101010101010101010101010101010101010101011

    注意此時小數點後有51位,這裡回答了第一個問題為什麼末尾是01011和第二個問題為什麼是奇數。
  • 符號為0,數字為正;

轉化為十進位,即2.3333333333333335.

第三個問題:1.0/3:

二進位定點數為:0.1010101010101010101010101010101010101010101010101010

  • 符號為正,表示為『0『;
  • 指數為-1(0.1010……變為01.010……),表示為』11111111110『;
  • 尾數表示為』0101010101010101010101010101010101010101010101010101『

    注意第52位為1,因為其本身就是1,第52位為0所以截斷時未發生進位。
  • 合起來:0111111111100101010101010101010101010101010101010101010101010101

換成十進位為0.3333333333333333.

PS:

感謝 @楊鑫逸 在我忙的時候幫我圓了場。

如果有1000本《哈姆雷特》,那麼就有1000位莎士比亞。

本莎士比亞哦不,本菜鳥所用方法出自弗羅贊《計算機科學導論(第三版)》第三章』數據存儲『,若有錯誤請及時提醒。


感謝 @msoeg 的提醒:

64位浮點數轉為十進位後有效數字為16位,所以第17位是不準確的。

在這裡向知友們提出一個思考題:

0.1+0.2 == 17位有效數字;

而0.1+0.7 == 16位有效數字。

這是為什麼?

python輸出位數不一致


十進位如何轉二進位?

有知友發私信問我2.3333...如何轉為10.010101...的,在這裡解答一下疑惑。

首先弄清楚:十進位3在二進位是11,沒錯。但是十進位0.3在二進位就不是0.11了。

在這裡為了更好地說明問題,我們取『22.33』來做實驗。

十進位小數轉化為二進位小數有兩部分:整數部分22和小數部分0.33。

先來處理整數部分『22』:

  1. 源為22,底為2,目標暫為空。
  2. 用底除源,商11餘0. 目標為『0』,新源為11.
  3. 用底除源,商5餘1,目標為『10』,新源為5.
  4. 用底除源,商2餘1,目標為『110』,新源為2.
  5. 用底除源,商1餘0,目標為『0110』,新源為1.
  6. 用底除源,商0餘1,目標為『10110』,結束。

二進位整數部分為『10110』。

整數部分轉換過程

再來處理小數部分『0.33』:

  1. 源為0.33,底為2,目標暫為空,我們把目標位數定為7位。
  2. 用底乘源,得0.66,目標為『0』,新源為0.66.
  3. 用底乘源,得1.32,目標為『01』,新源為0.32.
  4. 用底乘源,得0.64,目標為『010』,新源為0.64.
  5. 用底乘源,得1.28,目標為『0101』,新源為0.28.
  6. 用底乘源,得0.56,目標為『01010』,新源為0.56.
  7. 用底乘源,得1.12,目標為『010101』,新源為0.12.
  8. 用底乘源,得0.24,目標為『0101010』,結束。

二進位整數部分為0.0101010.

小數部分轉換過程

合起來:

得10110.0101010

PS:

此方法出自弗羅贊《計算機科學導論(第三版)》第二章『數字系統『,若有錯誤請及時提醒。


這種技術性回答有那麼多人瀏覽,有點出乎意料。

所以,滿足一下我滴好奇心:你們為什麼點進來看這種『枯燥』的技術問題?


來自系統內存尷尬而不失禮貌的微笑


就是四捨五入。只不過是二進位的四捨五入。學名截斷誤差


這不是python的問題,這個一方面是是實數的問題,另一方面是內存的問題。

實數是無限精度的,而內存是有限的。所以只能以誤差的形式表現出來。


你也可以試一試0.1+0.2這類的,總之只要是按照IEEE754標準來的浮點數極其演算法,都會出現你提的這個問題。

https://en.wikipedia.org/wiki/IEEE_754


浮點數和整數的問題


雙精度本身具有位數精度限制。如果想要精確計算結果,可以自己寫一個有理數類,或者從已有的庫中找有理數類、實數類等更精確的類。


有限的內存無法保存無限的小數位,所以要取一個近似值。

而內存是用二進位保存的,所以0.0000000004是很難表達的,而0.00000005就比較好表達了

我是這麼理解的。。。

因為二進位的話,小數位其實是用二的負n次存儲的。基本都是末尾和5有關的數字。


推薦閱讀:

TAG:Python | 浮點運算 |