PEP 682 – 有符號零的格式說明符
- 作者:
- John Belmonte <john at neggie.net>
- 發起人:
- Mark Dickinson <dickinsm at gmail.com>
- PEP 代理人:
- Mark Dickinson
- 討論至:
- Discourse 帖子
- 狀態:
- 最終版
- 型別:
- 標準跟蹤
- 建立日期:
- 2022年1月29日
- Python 版本:
- 3.11
- 釋出歷史:
- 2022年2月8日
- 決議:
- Discourse 帖子
摘要
儘管 float 和 Decimal 型別可以表示有符號零,但在許多數學領域中,負零是令人驚訝或不希望出現的——特別是在顯示(通常是四捨五入的)數值結果時。本PEP提議擴充套件字串格式規範,允許將負零規範化為正零。
動機
這是負零
>>> x = -0.
>>> x
-0.0
在格式化數字時,負零可能由四捨五入引起。假設使用者的意圖確實是丟棄精度,那麼四捨五入結果中負零和正零的區別可能被認為是不需要的副作用。
>>> for x in (.002, -.001, .060):
... print(f'{x: .1f}')
0.0
-0.0
0.1
有多種方法可以清除負零的符號。可以透過加正零實現,而無需條件判斷。
>>> x = -0.
>>> x + 0.
0.0
在格式化時規範化負零,需要對輸入進行冗餘(且容易出錯)的預四捨五入操作。
>>> for x in (.002, -.001, .060):
... print(f'{round(x, 1) + 0.: .1f}')
0.0
0.0
0.1
有充分的證據表明,無論使用何種語言,程式設計師們經常尋找抑制負零的方法,並採用了各種變通方案(預四捨五入、後正則表示式等)。舉例如下:
- 如何在Python字串中始終將負零格式化為正零? (Python,後正則表示式)
- (Iron)Python格式化問題,與模運算子和“負零”相關 (Python,預四捨五入)
- Java中零的負號問題 (Java,後正則表示式)
- 防止小負數列印為“-0” (Objective-C,自定義數字格式化器)
我們希望的是一個一流的選項來規範化負零,同時保留數值字串格式化已經提供的所有其他功能。
基本原理
在格式化數字輸出中,有時不希望出現負零——可以說,不希望出現的情況更常見。擴充套件格式規範是支援這一點的最佳方式,因為數字格式化已經包含了四捨五入,而負零的規範化必須在四捨五入之後進行。
雖然可以在格式化之前對數字進行預四捨五入和規範化,但這很繁瑣,並且如果四捨五入與格式說明符不完全匹配,則容易出錯。此外,包裝格式化的函式會發現自己不得不解析格式說明符以提取精度資訊。例如,考慮一下這種用於格式化一維數值陣列的實用工具將如何因這種預四捨五入而變得複雜。
def format_vector(v, format_spec='8.2f'):
"""Format a vector (any iterable) using given per-term format string."""
return f"[{','.join(f'{term:{format_spec}}' for term in v)}]"
迄今為止,似乎還沒有其他廣泛使用的語言或庫提供負零的格式化選項。然而,下面指定的相同 z 選項語法和語義已被提議用於 C++ std::format()。雖然該提案已在 C++20 中撤回,但 C++23 承諾將提供一個共識提案。(促成本 PEP 的原始功能請求是在不知曉 C++ 提案的情況下提出的。)
當Rust開發者們討論是否要在print輸出中抑制負零時,他們對其他語言進行了小型調查。值得注意的是,他們沒有提及任何提供負零處理選項的語言。
規範
在格式規範微語言中,sign 後面增加了一個可選的字面量 z。
[[fill]align][sign][z][#][0][width][grouping_option][.precision][type]
其中 z 允許用於浮點表示型別(f、g 等,如格式規範文件所定義)。對 z 的支援由每個數字型別的 .__format__() 方法提供,允許該說明符用於 f-字串、內建 format() 和 str.format()。
當存在 z 時,負零(無論是原始值還是四捨五入的結果)都將規範化為正零。
概要
>>> x = -.00001
>>> f'{x:z.1f}'
'0.0'
>>> x = decimal.Decimal('-.00001')
>>> '{:+z.1f}'.format(x)
'+0.0'
設計說明
該解決方案必須是可選的,因為我們不能改變可能期望或依賴負零的程式的行為,當它們在格式化數字時。
提議的擴充套件有意採用 [sign][z] 而不是 [sign[z]]。 sign 的預設值 (-) 並不廣為人知或明確書寫,因此這樣可以避免每個人為了使用 z 選項而不得不學習它。
雖然 f-字串、內建 format() 和 str.format() 可以訪問新選項,但 %-格式化不能。已經有不為 %-格式化擴充套件新選項的先例,例如 , 選項 (PEP 378) 就是如此。
C99 printf 已經將 z 選項字元用於另一個目的:限定無符號型別 (u) 以匹配 size_t 的長度。然而,由於有符號零選項明確禁止 z 用於整數表示型別,因此如果 C 希望採用此新選項,則可以區分這兩種用法。
向後相容性
新的格式化行為是可選的,因此現有程式的數值格式化不會受到影響。
如何教授此內容
典型的Python入門課程不會詳細講解字串格式化。對於此類課程,無需進行任何調整。對於深入講解字串格式規範的課程,一個示例足以說明 z 選項對經格式化後四捨五入為零的負值的影響。對於獨立開發者在他人程式碼中遇到此功能的情況,查閱庫參考手冊的格式規範微語言部分應該足夠了。
參考實現
參考實現可在拉取請求 #30049中找到。
版權
本文件置於公共領域或 CC0-1.0-Universal 許可證下,以更寬鬆者為準。
來源:https://github.com/python/peps/blob/main/peps/pep-0682.rst
最後修改時間:2025-02-01 08:55:40 GMT