HOWTO · Python
在 Python 中截斷浮點數
本教程說明了如何在 Python 中截斷浮點數
本頁內容
Python 的 float 無法原地縮短:它是二進位浮點數值,不是位數固定的小數字串。若要刪除指定小數位之後的數字,請從數字的十進位文字建立 Decimal,再使用 ROUND_DOWN 執行量化。若需要整數結果,請使用 math.trunc() 或 int()。這些操作都會朝零截斷;round() 和數值格式化的行為則不同。
選擇截斷或四捨五入
截斷會刪除指定位置之後的數字,並朝零的方向移動;四捨五入則依照取整規則選擇鄰近的數值。Python 的 round(number, ndigits) 會進行四捨五入,不會截斷。例如,round(3.146, 2) 會得到 3.15,截斷至小數點後兩位則會得到 3.14。Python 內建的 round() 在遇到剛好位於中間的情況時會採用「捨入至偶數」規則,因此不應將它視為通用的小數截斷方法。
將浮點數截斷至固定小數位數
若要保留指定的小數位數,請使用 Decimal。從字串表示建立它,再使用 ROUND_DOWN 呼叫 quantize()。在 decimal 模組中,ROUND_DOWN 代表朝零捨去:它會朝零刪除多餘位數,因此負數也會朝零移動,而不是變得更負。傳入 "3.14159" 這類字串,可以保留預期的十進位輸入;若直接從 float 建立 Decimal,取得的則是該浮點數的二進位近似值。
執行以下範例:
本節中的程式碼以及顯示的標籤或輸出仍為英文。
from decimal import Decimal, ROUND_DOWN
values = ("3.14159", "-3.14159", "0", "2.1")
places = 2
quantum = Decimal("1").scaleb(-places)
for text in values:
print(Decimal(text).quantize(quantum, rounding=ROUND_DOWN))
3.14
-3.14
0.00
2.10
前兩個結果展示了正數與負數都會朝零截斷。量化也會保留指定的小數位數:零會變成 0.00,而 2.1 會變成 2.10。如果手上已有浮點數,並想用它最短的顯示十進位表示作為輸入,請使用 Decimal(str(value));這無法還原建立浮點數之前已遺失的十進位資訊。處理金額或其他需要精確十進位值的數量時,最好一開始就接受十進位文字,或從整數與字串建立 Decimal。請參閱 decimal 模組文件。
朝零截斷為整數
若要取得整數,而非保留固定的小數位數,math.trunc() 會明確地朝零回傳整數部分。將浮點數轉換為整數時,內建函式 int() 也會做相同的處理。例如,math.trunc(5.67) 會得到 5,而 math.trunc(-3.45) 會得到 -3。這兩者都不會保留小數位;如果結果需要固定的小數位數,請使用 Decimal.quantize()。math.trunc() 也接受實作了 __trunc__() 的數值型別;該方法會提供截斷時使用的整數值。
為什麼縮放浮點數可能產生意外結果
一種常見的簡便做法,是將浮點數乘以 10 的次方、把結果轉成整數,再除以相同的數值。這對某些數值可行,但二進位浮點數無法精確表示許多十進位小數。因此,乘法結果可能略低於或略高於預期的十進位值,讓 int(value * 100) / 100 刪掉非預期的數字。結果也會是另一個浮點數,仍受二進位表示的限制。處理對十進位精度敏感的資料時,請避免依賴這種做法;改用搭配文字輸入的 Decimal。若計算本身允許近似值,則可繼續使用浮點數,並留意其精度限制,不要把顯示出的數字當成精確值。
選擇適合的方法
- 若要截斷至固定的小數位數,請從十進位文字建立
Decimal,並使用quantize(..., rounding=ROUND_DOWN)。 - 若要刪除小數部分並朝零取得整數,請使用
math.trunc()或int()。 - 若需要的是四捨五入而非截斷,請使用
round()。 - 若只需控制顯示文字,請使用格式化。像
f"{value:.2f}"這樣的 f-string 會產生字串,並對顯示值進行四捨五入;它不會改變或截斷底層的浮點數。
請依照所需結果選擇方法:指定精度的十進位值、整數、四捨五入後的值,或僅供顯示的格式化字串。