Thoughfloat andDecimal types can representsigned zero, in manyfields of mathematics negative zero is surprising or unwanted – especiallyin the context of displaying an (often rounded) numerical result. This PEPproposes an extension to thestring format specification allowing negativezero to be normalized to positive zero.
Here is negative zero:
>>>x=-0.>>>x-0.0
When formatting a number, negative zero can result from rounding. Assumingthe user’s intention is truly to discard precision, the distinction betweennegative and positive zero of the rounded result might be considered anunwanted artifact:
>>>forxin(.002,-.001,.060):...print(f'{x: .1f}') 0.0-0.0 0.1
There are various approaches to clearing the sign of a negative zero. Itcan be achieved without a conditional by adding positive zero:
>>>x=-0.>>>x+0.0.0
To normalize negative zero when formatting, it is necessary to performa redundant (and error-prone) pre-rounding of the input:
>>>forxin(.002,-.001,.060):...print(f'{round(x,1)+0.: .1f}') 0.0 0.0 0.1
There is ample evidence that, regardless of the language, programmers areoften looking for a way to suppress negative zero, and landing on avariety of workarounds (pre-round, post-regex, etc.). A sampling:
What we would like instead is a first-class option to normalize negativezero, on top of everything else that numerical string formatting alreadyoffers.
There are use cases where negative zero is unwanted in formatted numberoutput – arguably, not wanting it is more common. Expanding the formatspecification is the best way to support this because number formattingalready incorporates rounding, and the normalization of negative zero musthappen after rounding.
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 matchthat of the format spec. Furthermore, functions that wrap formatting wouldfind themselves having to parse format specs to extract the precisioninformation. For example, consider how this utility for formattingone-dimensional numerical arrays would be complicated by such pre-rounding:
defformat_vector(v,format_spec='8.2f'):"""Format a vector (any iterable) using given per-term format string."""returnf"[{','.join(f'{term:{format_spec}}'forterminv)}]"
To date, there doesn’t appear to be any other widely-used language or libraryproviding a formatting option for negative zero. However, the samezoption syntax and semantics specified below have beenproposed for C++std::format(). While the proposal was withdrawn for C++20, a consensusproposal is promised for C++23. (The originalfeature request promptingthis PEP was argued without knowledge of the C++ proposal.)
When Rust developers debated whether to suppress negative zero inprintoutput, they took a smallsurvey of other languages. Notably, it didn’tmention any language providing an option for negative zero handling.
An optional, literalz is added to theFormat Specification Mini-Language followingsign:
[[fill]align][sign][z][#][0][width][grouping_option][.precision][type]
wherez is allowed for floating-point presentation types (f,g,etc., as defined by the format specification documentation). Support forz is provided by the.__format__() method of each numeric type,allowing the specifier to be used in f-strings, built-informat(), andstr.format().
Whenz is present, negative zero (whether the original value or theresult of rounding) will be normalized to positive zero.
Synopsis:
>>>x=-.00001>>>f'{x:z.1f}''0.0'>>>x=decimal.Decimal('-.00001')>>>'{:+z.1f}'.format(x)'+0.0'
The solution must be opt-in, because we can’t change the behavior ofprograms that may be expecting or relying on negative zero when formattingnumbers.
The proposed extension is intentionally[sign][z] rather than[sign[z]]. The default forsign (-) is not widely known orexplicitly written, so this avoids everyone having to learn it just to usethez option.
While f-strings, built-informat(), andstr.format() can accessthe new option, %-formatting cannot. There is already precedent for notextending %-formatting with new options, as was the case for the, option (PEP 378).
C99printf already uses thez option character for anotherpurpose: qualifying the unsigned type (u) to match the length ofsize_t. However, since the signed zero option specifically disallowsz for integer presentation types, it’s possible to disambiguate the twouses, should C want to adopt this new option.
The new formatting behavior is opt-in, so numerical formatting of existingprograms will not be affected.
A typical introductory Python course will not cover string formattingin full detail. For such a course, no adjustments would need to be made.For a course that does go into details of the string format specification,a single example demonstrating the effect of thez option on a negativevalue that’s rounded to zero by the formatting should be enough. For anindependent developer encountering the feature in someone else’s code,reference to theFormat Specification Mini-Language section of thelibrary reference manual should suffice.
A reference implementation exists atpull request #30049.
This document is placed in the public domain or under theCC0-1.0-Universal license, whichever is more permissive.
Source:https://github.com/python/peps/blob/main/peps/pep-0682.rst
Last modified:2025-02-01 08:55:40 GMT