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
输出中抑制负零时,他们进行了一次小型其他语言调查。值得注意的是,它没有提到任何语言提供负零处理选项。
规范
一个可选的、字面上的z
添加到格式规范迷你语言中,位于sign
之后。
[[fill]align][sign][z][#][0][width][grouping_option][.precision][type]
其中z
允许用于浮点表示类型(f
、g
等,如格式规范文档中所定义)。每个数值类型的.__format__()
方法提供了对z
的支持,允许在 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
上次修改时间:2023年9月9日 17:39:29 GMT