-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
PEP 682: Format Specifier for Signed Zero #2295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
f8b927c
PEP: Format Specifier for Signed Zero
belm0 a6bf8f3
add Python-Version
belm0 e1ad4f9
Apply suggestions from code review
belm0 3fbc1f6
Apply suggestions from code review
belm0 7a86237
suggested change: Background -> Motivation
belm0 6522671
PR feedback:
belm0 beb801d
correct ordering of Rationale and Specification
belm0 8a58924
rename file
belm0 8fd0dd7
correct type support
belm0 138ff46
Merge branch 'main' into signed_zero_format
JelleZijlstra 0aadde6
Apply suggestions from code review
belm0 866094b
remove "for what it's worth"; formatting
belm0 e1ae1fa
* fix abstract commas
belm0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -642,3 +642,4 @@ pep-8016.rst @njsmith @dstufft | |
| pep-8100.rst @njsmith | ||
| # pep-8101.rst | ||
| # pep-8102.rst | ||
| pep-9999.rst @mdickinson | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| PEP: 9999 | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Title: Format Specifier for Signed Zero | ||
| Author: John Belmonte <john@neggie.net> | ||
| Sponsor: Mark Dickinson | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Discussions-To: https://bugs.python.org/issue45995 | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Status: Draft | ||
| Type: Standards Track | ||
| Content-Type: text/x-rst | ||
| Created: 29-Jan-2022 | ||
| Python-Version: 3.11 | ||
| Post-History: | ||
|
|
||
|
|
||
| Abstract | ||
| ======== | ||
|
|
||
| Though float and Decimal types can represent `signed zero`_, in many fields | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| of mathematics negative zero is surprising or unwanted-- especially in the | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| in the context of displaying an (often rounded) numerical result. This PEP | ||
| proposes an extension to the string format specification, allowing negative | ||
| zero to be normalized to positive zero. | ||
|
|
||
| .. _`signed zero`: https://en.wikipedia.org/wiki/Signed_zero | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| Background | ||
| ========== | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Here is negative zero:: | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| >>> x = -0. | ||
| >>> x | ||
| -0.0 | ||
|
|
||
| When formatting a number, negative zero can result from rounding. Assuming | ||
| the user's intention is truly to discard precision, the distinction between | ||
| negative and positive zero of the rounded result might be considered an | ||
| unwanted artifact:: | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| >>> for x in (.002, -.001, .060): | ||
| ... print(f'{x: .1f}) | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 0.0 | ||
| -0.0 | ||
| 0.1 | ||
|
|
||
| There are various approaches to clearing the sign of a negative zero. It | ||
| can be achieved without a conditional by adding positive zero:: | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| >>> x = -0. | ||
| >>> x + 0. | ||
| 0.0 | ||
|
|
||
| To normalize negative zero when formatting, it would be necessary to perform | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| a redundant (and error-prone) pre-rounding of the input:: | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| >>> for x in (.002, -.001, .060): | ||
| ... print(f'{round(x, 1) + 0.: .1f}') | ||
| 0.0 | ||
| 0.0 | ||
| 0.1 | ||
|
|
||
| The behavior of number formatting with regard to negative zero is not | ||
| unique to Python. The first low-level implementations of ``printf()`` | ||
| and such likely wired the sign bit to ``-`` in the output without special | ||
| consideration of the zero mantissa case. In hindsight, it would have | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| been nice to have a formatting option for treatment of negative zero-- | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| perhaps even coercing it to positive zero by default. However, for better | ||
| or worse, existing number formatting implementations inherit from the | ||
| original specimens, passing the zero sign through without option. | ||
|
|
||
|
|
||
| Proposal | ||
| ======== | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| An optional, literal ``z`` is added to the | ||
| `Format Specification Mini-Language`_ following ``sign``:: | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] | ||
|
|
||
| where ``z`` is allowed only for float and Decimal types. The new specifier | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| is supported by both f-strings and ``str.format()``, but not %-formatting. | ||
|
|
||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Synopsis:: | ||
CAM-Gerlach marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| >>> x = -.00001 | ||
| >>> f'{x:z.1f}' | ||
| '0.0' | ||
|
|
||
| >>> x = decimal.Decimal('-.00001') | ||
| >>> '{}'.format('+z.1f') | ||
| '+0.0' | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| >>> x = 0 | ||
| >>> f'{x:z.1f}' # not supported on int type | ||
| ValueError: Negative zero coercion (z) not allowed in int format specifier | ||
|
|
||
| >>> x = -.00001 | ||
| >>> '%z.1f' % x # not supported on %-formatting | ||
| ValueError: unsupported format character 'z' (0x7a) at index 1 | ||
|
|
||
| .. _`Format Specification Mini-Language`: https://docs.python.org/3/library/string.html#format-specification-mini-language | ||
|
|
||
|
|
||
| Rationale | ||
| ========= | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| There are use cases where negative zero is unwanted in formatted number | ||
| output-- arguably it's the more common case. Expanding the format | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| specification is the best way to support this because number formatting | ||
| already incorporates rounding, and the normalization of negative zero must | ||
| happen after the rounding. | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| While it is possible to pre-round and normalize a number before formatting, | ||
| it's tedious and prone to error if the rounding doesn't precisely match | ||
| that of the format spec. Furthermore, functions that wrap formatting would | ||
| find themselves having to parse format specs to extract the precision | ||
| information. For example, consider how this utility for formatting | ||
| one-dimensional numerical arrays would be complicated by such pre-rounding:: | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| def format_vector(v, format_spec='8.2f'): | ||
| """Format a vector (any iterable) using given per-term format string""" | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return f"[{','.join(f'{term:{format_spec}}' for term in v)}]" | ||
|
|
||
| The solution must be opt-in, because we can't change the behavior of | ||
| programs that may be expecting or relying on negative zero when formatting | ||
| numbers. | ||
|
|
||
| The proposed extension is intentionally ``[sign][z]`` rather than | ||
| ``[sign[z]]``, since the latter would defeat ``sign``'s default (``-``) | ||
| when using the ``z`` option. Precisely because it is the default, ``-`` is | ||
| not well known, and requiring it would increase the difficulty for those | ||
| using or encountering the new option. | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| There is a long-standing decision not to enhance the old %-formatting, so | ||
| only f-strings and ``str.format()`` support the new option. | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| Similar Work | ||
| ============ | ||
|
|
||
| The same formatting option for negative zero has been `proposed for | ||
| C++ std::format()`_. While the proposal was withdrawn for C++20, a | ||
| consensus proposal is promised for C++23. (For what it's worth, the original | ||
| feature request prompting this PEP was argued without knowledge of the | ||
| C++ proposal.) | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. _`proposed for C++ std::format()`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1496r2.pdf | ||
|
|
||
|
|
||
| Reference Implementation | ||
| ======================== | ||
|
|
||
| A reference implementation exits at `pull request #30049`_. | ||
belm0 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. _`pull request #30049`: https://github.com/python/cpython/pull/30049 | ||
|
|
||
|
|
||
| Copyright | ||
| ========= | ||
|
|
||
| This document is placed in the public domain or under the | ||
| CC0-1.0-Universal license, whichever is more permissive. | ||
|
|
||
|
|
||
|
|
||
| .. | ||
| Local Variables: | ||
| mode: indented-text | ||
| indent-tabs-mode: nil | ||
| sentence-end-double-space: t | ||
| fill-column: 70 | ||
| coding: utf-8 | ||
| End: | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.