Python技法:浮點數取整、格式化和NaN處理

来源:https://www.cnblogs.com/orion-orion/archive/2022/05/21/16296007.html
-Advertisement-
Play Games

強轉int類型會直接對浮點數的小數部分進行截斷(無論是正還是負)。還有一種方法是math.ceil和math.floor。無論是正數還是負數,都遵循:ceil往數軸正方向取整,floor往數軸負方向取整。round原型為round(value, ndigits),可以將一個浮點數取整到固定的小數位。... ...


1. 取整的三種方法

1.1 強轉int類型

這種方法會直接對浮點數的小數部分進行截斷(無論是正還是負)。

print(int(2.7)) # 2
print(int(-2.7)) # -2

1.2 採用math.ceil和math.floor

這種方法的取整規則如下圖所示:

可以看到無論是正數還是負數,都遵循:ceil往數軸正方向取整,floor往數軸負方向取整。實例如下:

print(math.ceil(-1.27)) # -1
print(math.floor(-1.27)) # -2
print(math.ceil(1.27)) # 2
print(math.floor(1.27)) # 1

1.3 採用round

round原型為round(value, ndigits),可以將一個浮點數取整到固定的小數位。該函數對正數和負數都採取就近取整原則,而當某個值恰好等於兩個整數間一半時,取整操作會取到離該值最近的那個偶數。像1.5和2.5這樣的值都會取整到2。示例如下:

print(round(1.23, 0)) # 1.0
print(round(1.23, 1)) # 1.2
print(round(1.27, 1)) # 1.3
print(round(-1.27, 1)) # -1.3
print(round(1.25361, 3)) # 1.254
print(round(1.5, 0)) # 2.0
print(round(2.5, 0)) # 2.0

傳遞給round()參數ndigits可以是負數,這種情況下回相應取整到十位、百位、千位:

a = 1627731
print(round(a, -1)) # 1627730
print(round(a, -2)) # 1627700
print(round(a, -3)) # 1628000

2. 格式化浮點數輸出

註意對值輸出時別把取整和格式化操作混為一談。如果只是將數值以固定位數輸出,一般是用不著round()的,只要在用format格式化時指定所需要的精度即可(format()格式化操作會根據round()的規則進行取整,最終返回一個字元串類型)。

x = 1234.56789
s = format(x, "0.2f")
print(type(s), format(x, "0.2f")) # <class 'str'> 1234.57

除了取整到固定小數位,format()還具有許多格式化功能,如格式化輸出對齊,增加千分位分隔符等。實際上面的0.2f就表示至少對齊到0個字元(相當於沒有對齊操作),並保留兩位小數。

小提示:.2f也表示至少對齊到0個字元(預設是0),並保留兩位小數,
0.2f二者是等效的。

更多示例如下:

# 往右調整以對齊到10個字元
print(format(x, ">10.1f")) #     1234.6
# 往右調整以對齊到10個字元
print(format(x, "<10.1f")) # 1234.6    
# 居中以對齊到10個字元
print(format(x, "^10.1f")) #   1234.6  
# 增加千位分隔符
print(format(x, ",")) # 1,234.56789
# 增加千位分隔符並保存到1位小數
print(format(x, "0,.1f")) # 1,234.6

如果想使用科學計數法,只要把f改成eE即可:

print(format(x, "e")) # 1.234568e+03

print(format(x, "0.2E")) # 1.23E+03

此外,我們還可以利用字元串的translate()方法交換不同的分隔符:

swap_separators = {ord("."):",", ord(","):"."}
print(format(x, ",").translate(swap_separators)) # 1.234,56789

最後,我們這裡提一下,調用字元串的.format()函數和單獨調用format()函數可以達到相同的效果,如:

print("value is {:0.3f}".format(x)) # value is 1.235
print("The value is {:0,.2f}".format(x)) # The value is 1,234.57

當然我們也可以使用%操作符來對數值做格式化處理,如:

print("%.2f" % x)

print("%10.1f" % x)

print("%-10.1f" % x)

這種格式化操作雖然可行,但是比起更加現代化的format()方法,這種方法就顯得不是那麼強大了。如用%操作符來格式化數值時,有些功能就沒法得到支持了(如添加千位分隔符)。

3. 執行精確的小數計算

我們在第一部分介紹了round()函數,我們有可能會企圖用浮點取整的方式來“修正”精度上的問題,如:

a = 2.1
b = 4.2
c = a + b
print(c) # 6.300000000000001
print(c==6.3) # False
print(round(c, 2)) # 6.3 企圖這樣修正精度(???)

對大部分浮點數應用程式(包括科學計算與機器學習)來說,一般都不必(或者所不推薦)這麼做。雖然Python的浮點運算會引入一些小誤差,但這些誤差實際上是底層CPU的浮點運算單元和IEEE 754浮點算數標準的一種“特性”。由於Python的浮點數類型保存的數據採用的是原始保存形式,因此只要代碼中用到了float實例,那就無法避免這樣的誤差。

如果避免出現誤差的行為非常重要(比如在金融應用中),那麼可以考慮使用decimal模塊。事實上在用Python做資料庫庫介面時經常碰到Decimal對象——當訪問金融數據時尤其如此。我們通過使用Decimal對象解決上述問題:

from decimal import Decimal

a = Decimal('4.2')
b = Decimal('2.1')
print(type(a + b), a + b) # <class 'decimal.Decimal'> 6.3
print((a + b) == Decimal('6.3')) # True

這麼做看起來似乎有點怪異(將數字以字元串的形式來指定)。但是Decimal對象能夠以任何期望的方式來工作(支持所有常見的數學操作)。如果要將它們列印出來或者在字元串格式化函數中使用,它們看起來就和普通數字一樣。它們也可以和普通intfloat類型混合操作(最後會統一強轉為Decimal類型):

print(type(a + 1), a + 1) # <class 'decimal.Decimal'> 5.2

但是需要註意的是不要將其與普通float類型直接進行比較:

print((a + b) == 6.3) # False

decimal模塊的強大之處在於在計算過程中靈活地控制數字的位數和四捨五入,如我們可以創建一個本地的上下文環境然後修改精度的設定,如:

from decimal import localcontext
a = Decimal("1.3")
b = Decimal("1.7")
print(a/b) # 0.7647058823529411764705882353
with localcontext() as ctx:
    ctx.prec = 3
    print(a/b) # 0.765 

with localcontext() as ctx:
    ctx.prec = 50
    print(a/b) # 0.764705882352941176470588235294117647058823529

不過還是我們上面所說的,如果我們處理的是科學或工程類型的問題,那麼更常見的做法是直接使用普通的float浮點類型。首先,在真實世界中極少有東西需要計算到小數點後17位(float提供17位的精度),因此在計算中引入的微小誤差不足掛齒;其次,原生的float浮點數運算性能要快許多——如果要執行大量計算,性能問題就顯得很重要了。

在使用float類型時,我們同樣還需要對類似相減抵消(substraction cancellation)以及把大數和小數載入一起的情況多加小心:

nums = [1.23e+18, 1, -1.23e+18]
print(sum(nums)) # 0.0

使用Decimal對象當然可以解決此問題。不過在不動用Decimal對象的情況下,我們可以使用math.fsum()以更精確的實現來解決:

import math
print(math.fsum(nums)) # 1.0

但對於其它複雜的數值演算法,我們就需要研究演算法本身,理解其誤差傳播(error propagation)了,這屬於數值分析的研究範疇。在數值分析中數學家研究了大量數值演算法,其中一些演算法的誤差處理能力優於其它演算法,詳情可以參見我的數值計算專欄《orion-orion:數值計算》,此處不再詳述。

4. 無窮大、負無窮大和NaN的判斷測試

在實際項目中我們需要對浮點數的無窮大、負無窮大或NaN(not a number)進行判斷測試。在Python中沒有特殊的語法來表示這些特殊的浮點值,但是它們可以通過float來創建:

a = float("inf")
b = float("-inf")
c = float("nan")
print(a, b, c) # inf -inf nan

要檢查是否出現了這些值,可以使用math.isinf()math.isnan()函數:

print(math.isinf(a)) # True
print(math.isnan(c)) # True

這些特殊浮點數的詳細信息可以參考IEEE 754規範。但是我們這裡有幾個棘手的問題需要搞清楚,尤其是設計比較操作和操作符時可能出現的問題。

無窮大值在數學計算中會進行傳播,如:

a = float("inf")
print(a + 45) # inf
print(a * 10) # inf
print(10/a) # 0.0

但是,某些關於無窮大值特定的操作會導致未定義的行為並產生NaN的結果,例如:

a = float("inf")
print(a/a) # nan
b = float("-inf")
print(a + b) # nan

NaN會通過所有的操作進行傳播,且不會引發任何異常,如:

c = float("nan")
print(c + 23) # nan
print(c / 2) # nan
print(c + 2) # nan

有關NaN,一個微妙的特性是他們在做比較時從不會被判定為相等,如:

c = float("nan")
d = float("nan")
print(c == d) # False
print(c is d) # False

正因為如此,唯一安全檢測NaN的方法是使用math.isnan()

參考

數學是符號的藝術,音樂是上界的語言。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 導讀: 線上社交媒體平臺的發展,帶來了細粒度檢索、視頻語義摘要等媒體智能服務的巨大需求。現有的視頻理解技術缺乏深入的語義線索,結合視頻中人物的社交關係才能更完整、準確地理解劇情,從而提升用戶體驗,支撐智能應用。這裡主要介紹我們將動態分析和圖機器學習相結合,圍繞視頻中的人物社交關係網路所開展的兩個最新 ...
  • 問題描述:發現主庫操作數據從庫沒有變動問題,可能原因是從庫重啟導致的無法同步問題。 排查思路: 1、查看主從複製狀態 發現從庫的IO和SQL進程都是no(正常狀態應該是yes) 註意:mysql replication中slave機器上有兩個關鍵進程,死一個都不行,一個是slave_sql_runn ...
  • Toolbar 新建項目會預設顯示ActionBar,ActionBar根據項目指定的主題來顯示,android:theam指定主題,在res/values/theams.xml中定義主題,因為用toolbar代替ActionBar,所以將parent主題設置為不帶ActionBar的主題,如下: ...
  • 在瀏覽器測試JavaScript的方法 JSFiddle Cross Browser Testing Tool Karam + Jasmine + Google Chrome CodePen JSBin Liveweave 測試JavaScript是一件很痛苦的事情。很多工具、技術和框架已經被開發出 ...
  • 摘要:本篇我們將以分析歷史股價為例,介紹怎樣從文件中載入數據,以及怎樣使用NumPy的基本數學和統計分析函數、學習讀寫文件的方法,並嘗試函數式編程和NumPy線性代數運算,來學習NumPy的常用函數。 一、文件讀入 :讀寫文件是數據分析的一項基本技能 CSV(Comma-Separated Valu ...
  • 轉載請註明出處❤️ 作者:IT小學生蔡坨坨 原文鏈接:https://www.caituotuo.top/a3a8d0c0.html 大家好,我是IT小學生蔡坨坨。 今天,我們來聊聊介面自動化測試是什麼?如何開始?介面自動化測試框架怎麼做? 自動化測試 自動化測試,這幾年行業內的熱詞,也是測試人員進 ...
  • 知識回顧 Bean的創建過程會經歷getBean,doGetBean,createBean,doCreateBean,然後Bean的創建又會經歷實例化,屬性填充,初始化。 在實例化createInstance時大致可以分為三種方式進行實例化: 使用Supplier 進行實例化,通過BeanFacto ...
  • 在上一篇文章《SpringBoot進階教程(七十三)整合elasticsearch 》,已經詳細介紹了關於elasticsearch的安裝與使用,現在主要來看看關於ELK的定義、安裝及使用。 v簡介 1.什麼是ELK? ELK 是elastic公司提供的一套完整的日誌收集以及展示的解決方案,是三個產 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文將以 C# 語言來實現一個簡單的布隆過濾器,為簡化說明,設計得很簡單,僅供學習使用。 感謝@時總百忙之中的指導。 布隆過濾器簡介 布隆過濾器(Bloom filter)是一種特殊的 Hash Table,能夠以較小的存儲空間較快地判斷出數據是否存在。常用於允許一定誤判率的數據過濾及防止緩存 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 「簡單有價值的事情長期堅持做」 這是成功最簡單,但也最難學的秘訣。不經過訓練,人很難意識到時間複利的威力。 仙劍奇俠傳的「十里坡劍神」和金庸群俠傳的「十級野球拳」,就是簡單的事情持之以恆反覆做,最後就有巨大的威力 唐家三少成為網文收入第一,最重要的一步是十四年從未斷日更 這樣的案例很多,一開始可能成 ...
  • 迎面走來了你的面試官,身穿格子衫,挺著啤酒肚,髮際線嚴重後移的中年男子。 手拿泡著枸杞的保溫杯,胳膊夾著MacBook,MacBook上還貼著公司標語:“我愛加班”。 面試開始,直入正題。 面試官: 看你簡歷上面寫著精通MySQL,我先問你事務的特性是什麼? 老生常談,這個還有誰不會背的嗎? 我: ...
  • 基礎知識 python是一門腳本語言,它是解釋執行的。 python使用縮進做為語法,而且python2環境下同一個py文件中不能同時存在tab和空格縮進,否則會出錯,建議在IDE中顯示縮進符。 python在聲明變數時不寫數據類型,可以type(xx)來獲取欄位的類型,然後可以int(),list ...
  • 為什麼要多線程下載 俗話說要以終為始,那麼我們首先要明確多線程下載的目標是什麼,不外乎是為了更快的下載文件。那麼問題來了,多線程下載文件相比於單線程是不是更快? 對於這個問題可以看下圖。 橫坐標是線程數,縱坐標是使用對應線程數下載對應文件時花費的時間,藍橙綠代表下載文件的大小,每個線程下載對應文件2 ...
  • 詳細講解python爬蟲代碼,爬微博搜索結果的博文數據。 爬取欄位: 頁碼、微博id、微博bid、微博作者、發佈時間、微博內容、轉發數、評論數、點贊數。 爬蟲技術: 1、requests 發送請求 2、datetime 時間格式轉換 3、jsonpath 快速解析json數據 4、re 正則表達式提... ...
  • 背景: 一般我們可以用HashMap做本地緩存,但是HashMap功能比較弱,不支持Key過期,不支持數據範圍查找等。故在此實現了一個簡易的本地緩存,取名叫fastmap。 功能: 1.支持數據過期 2.支持等值查找 3.支持範圍查找 4.支持key排序 實現思路: 1.等值查找採用HashMap2 ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 本章是系列文章的第八章,用著色演算法進行寄存器的分配過程。 本文中的所有內容來自學習DCC888的學習筆記或者自己理解的整理,如需轉載請註明出處。周榮華@燧原科技 寄存器分配 寄存器分配是為程式處理的值找到存儲位置的問題 這些值可以存放到寄存器,也可以存放在記憶體中 寄存器更快,但數量有限 記憶體很多,但 ...