Python浮點數四捨五入問題的分析與解決方法

2021-10-24 19:34:07 字數 2488 閱讀 8150

昨天遇到乙個問題,在 6.6045 保留三位小數時,使用 round() 函式進行計算,我們希望得到 6.605,然而:

>>> round(6.6045, 3)

6.604

網上有人說,因為在計算機裡面,小數是不精確的,例如 1.115 在計算機中實際上是 1.114999999999999991182,所以當你對這個小數精確到小數點後兩位的時候,實際上小數點後第三位是 4,所以四捨五入,結果為 1.11.

這種說法,對了一半。

因為並不是所有的小數在計算機中都是不精確的。例如 0.125 這個小數在計算機中就是精確的,它就是 0.125,沒有省略後面的值,沒有近似,它確確實實就是 0.125.

但是如果我們在 python 中執行:

>>> round(0.125, 2)

0.12

為什麼在這裡四捨了?

還有更奇怪的,另乙個在計算機裡面能夠精確表示的小數 0.375,我們來看看精確到小數點後兩位是多少:

>>> round(0.375, 2)

0.38

為什麼在這裡又五入了?

因為在 python3 裡面,round 對小數的精確度採用了四捨六入五成雙的方式。

如果你寫過大學物理的實驗報告,那麼你應該會記得老師講過,直接使用四捨五入,最後的結果可能會偏高,所以需要使用奇進偶舍的處理方法。

例如對於乙個浮點數 a.bcd,需要精確到小數點後兩位,那麼就要看小數點後第三位:

關於奇進偶舍,有興趣的朋友可以在維基百科搜尋這兩個詞條:數值修約和奇進偶舍。

所以,round 給出的結果如果跟設想的不一樣,那麼需要考慮兩個原因:

你的這個小數在計算機中能不能被精確儲存?如果不能,那麼它可能並沒有達到四捨五入的標準,例如 1.115,它的小數點後第三位實際上是 4,當然會被捨去。

如果你的這個小數在計算機中能被精確表示,那麼,round 採用的進製機制是奇進偶舍,所以這取決於你要保留的那一位,它是奇數還是偶數,以及它的下一位後面還有沒有資料。

關於奇進偶舍,有興趣的朋友可以在搜尋這兩個詞條:數值修約和奇進偶舍

回到最開始的問題,對於 6.6045 這個浮點數,我們在 scheme 中檢視一下它的精確形式:

> (exact 6.6045)

3718002967371055/562949953421312

也就是說它是不能被精確儲存的,大概表現為 6.60449999999999…的形式,因此四捨五入的時候得到了 6.604。

如果要實現數學上的四捨五入,那麼就需要使用decimal模組,具體用法參考官方文件:

其中 quantize 的函式原型和文件說明,提到了可以通過指定 rounding 引數來確定進製方式。如果沒有指定 rounding 引數,那麼會預設使用上下文提供的進製方式。

現在我們來檢視一下預設的上下文中的進製方式是什麼:

>>> from decimal import getcontext

>>> getcontext().rounding

'round_half_even'

round_half_even實際上就是奇進偶舍,

如果要指定真正的四捨五入,那麼我們需要在 quantize 中指定進製方式為round_half_up

>>> from decimal import decimal, round_half_up

>>> decimal('0.125').quantize(decimal('0.00'), rounding=round_half_up)

decimal('0.13')

現在看起來一切都正常了。

有人可能會進一步追問一下,如果 decimal 接收的引數不是字串,而是浮點數會怎麼樣呢?

來實驗一下:

>>> decimal(0.125)

decimal('0.125')

那是不是說明,在 decimal 的第乙個引數,可以直接傳浮點數呢?

我們換乙個數來測試一下:

>>> decimal(11.245)

decimal('11.2449999999999992184029906638897955417633056640625')

浮點數 11.245 和字串'11.245'傳進去以後的結果居然不一樣。

我們繼續在文件中尋找答案:

官方文件已經很清楚地說明了:

php浮點數四捨五入函式

在php中浮點數四捨五入的兩個常用的函式round與此同時ceil函式,可能小數保留位數.round 函式對浮點數進行四捨五入 語法 float round float val int precision 返回將 val 根據指定精度 precision 十進位制小數點後數字的數目 進行四捨五入的結...

float浮點數的四捨五入

前幾天,有個小夥伴在做實驗過程中,發現了乙個奇怪的現象,這個現象就是 他在用printf輸出浮點數的時候,想把資料保留到小數點後的兩位,他是這麼寫的 float c 1.155 printf 2f c 他的書寫是對的,沒有錯誤。但是他發現,當c等於1.555時,保留兩位小數輸出是1.55,而當c等於...

python浮點數四捨五入的問題的分析

python中round做四捨五入要說清楚並不容易。有一種奇進偶舍的說法並不準確。下面的例子就無法用奇進偶舍來解釋 print round 1.275,2 1.27 原因分析 float採用二進位制編碼描述浮點數。在二進位制表示中,大多數有限位十進位制小數無法使用有限位二進位制進行精確表示。也就是說...