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