From f8b927ce9ded1f0644c2cc6c3f0b8ded701a6c02 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sun, 30 Jan 2022 22:33:56 +0900 Subject: [PATCH 01/12] PEP: Format Specifier for Signed Zero --- .github/CODEOWNERS | 1 + pep-9999.rst | 172 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 pep-9999.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9f05d6c3302..68221fa2d13 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -642,3 +642,4 @@ pep-8016.rst @njsmith @dstufft pep-8100.rst @njsmith # pep-8101.rst # pep-8102.rst +pep-9999.rst @mdickinson diff --git a/pep-9999.rst b/pep-9999.rst new file mode 100644 index 00000000000..f16de84447b --- /dev/null +++ b/pep-9999.rst @@ -0,0 +1,172 @@ +PEP: 9999 +Title: Format Specifier for Signed Zero +Author: John Belmonte +Sponsor: Mark Dickinson +Discussions-To: https://bugs.python.org/issue45995 +Status: Draft +Type: Standards Track +Content-Type: text/x-rst +Created: 29-Jan-2022 +Post-History: + + +Abstract +======== + +Though float and Decimal types can represent `signed zero`_, in many fields +of mathematics negative zero is surprising or unwanted-- especially in the +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 + + +Background +========== + +Here is negative zero:: + + >>> 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:: + + >>> for x in (.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. It +can be achieved without a conditional by adding positive zero:: + + >>> x = -0. + >>> x + 0. + 0.0 + +To normalize negative zero when formatting, it would be necessary to perform +a redundant (and error-prone) pre-rounding of the input:: + + >>> 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 +been nice to have a formatting option for treatment of negative zero-- +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 +======== + +An optional, literal ``z`` is added to the +`Format Specification Mini-Language`_ following ``sign``:: + + [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] + +where ``z`` is allowed only for float and Decimal types. The new specifier +is supported by both f-strings and ``str.format()``, but not %-formatting. + +Synopsis:: + + >>> x = -.00001 + >>> f'{x:z.1f}' + '0.0' + + >>> x = decimal.Decimal('-.00001') + >>> '{}'.format('+z.1f') + '+0.0' + + >>> 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 +========= + +There are use cases where negative zero is unwanted in formatted number +output-- arguably it's the more common case. Expanding the format +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. + +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:: + + 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)}]" + +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. + +There is a long-standing decision not to enhance the old %-formatting, so +only f-strings and ``str.format()`` support the new option. + + +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.) + +.. _`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`_. + +.. _`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: From a6bf8f34f6c96a86863a7edecfefb2c36094f2ea Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Wed, 2 Feb 2022 23:07:36 +0900 Subject: [PATCH 02/12] add Python-Version --- pep-9999.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/pep-9999.rst b/pep-9999.rst index f16de84447b..3f2d11050c1 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -7,6 +7,7 @@ Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 29-Jan-2022 +Python-Version: 3.11 Post-History: From e1ad4f9e1868b04af43485d32c3f3b6e48d70372 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Thu, 3 Feb 2022 08:47:38 +0900 Subject: [PATCH 03/12] Apply suggestions from code review Co-authored-by: CAM Gerlach Co-authored-by: Hugo van Kemenade Co-authored-by: Jelle Zijlstra --- pep-9999.rst | 57 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/pep-9999.rst b/pep-9999.rst index 3f2d11050c1..4fb4b6820e7 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -14,19 +14,22 @@ Post-History: Abstract ======== -Though float and Decimal types can represent `signed zero`_, in many fields -of mathematics negative zero is surprising or unwanted-- especially in the +Though ``float`` and ``Decimal`` types can represent `signed zero`_, in many fields +of mathematics, negative zero is surprising or unwanted -- especially in the context of displaying an (often rounded) numerical result. This PEP -proposes an extension to the string format specification, allowing negative +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 +.. _`signed zero`: https://en.wikipedia.org/wiki/Signed_zero +.. _`string format specification`: https://docs.python.org/3/library/string.html#formatstrings Background ========== -Here is negative zero:: +Here is negative zero: + +.. code-block:: pycon >>> x = -0. >>> x @@ -35,23 +38,29 @@ Here is negative zero:: 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:: +unwanted artifact: + +.. code-block:: pycon >>> for x in (.002, -.001, .060): - ... print(f'{x: .1f}) + ... print(f'{x: .1f}') 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:: +can be achieved without a conditional by adding positive zero: + +.. code-block:: pycon >>> x = -0. >>> x + 0. 0.0 -To normalize negative zero when formatting, it would be necessary to perform -a redundant (and error-prone) pre-rounding of the input:: +To normalize negative zero when formatting, it is necessary to perform +a redundant (and error-prone) pre-rounding of the input: + +.. code-block:: pycon >>> for x in (.002, -.001, .060): ... print(f'{round(x, 1) + 0.: .1f}') @@ -63,24 +72,28 @@ 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 -been nice to have a formatting option for treatment of negative zero-- +been nice to have a formatting option for treatment of negative zero -- 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 -======== +Specification +============= An optional, literal ``z`` is added to the -`Format Specification Mini-Language`_ following ``sign``:: +`Format Specification Mini-Language`_ following ``sign``: + +.. code-block:: text [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] -where ``z`` is allowed only for float and Decimal types. The new specifier +where ``z`` is allowed only for ``float`` and ``Decimal`` types. The new specifier is supported by both f-strings and ``str.format()``, but not %-formatting. -Synopsis:: +Synopsis: + +.. code-block:: pycon >>> x = -.00001 >>> f'{x:z.1f}' @@ -105,20 +118,22 @@ Rationale ========= There are use cases where negative zero is unwanted in formatted number -output-- arguably it's the more common case. Expanding the format +output -- arguably it's the more common case. Expanding the format 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. +happen 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 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:: +one-dimensional numerical arrays would be complicated by such pre-rounding: + +.. code-block:: python def format_vector(v, format_spec='8.2f'): - """Format a vector (any iterable) using given per-term format string""" + """Format a vector (any iterable) using given per-term format string.""" 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 @@ -150,7 +165,7 @@ C++ proposal.) Reference Implementation ======================== -A reference implementation exits at `pull request #30049`_. +A reference implementation exists at `pull request #30049`_. .. _`pull request #30049`: https://github.com/python/cpython/pull/30049 From 3fbc1f6e3ea42b6f36379a4d237a8c125c885a2a Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Fri, 4 Feb 2022 08:58:56 +0900 Subject: [PATCH 04/12] Apply suggestions from code review fix example Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- pep-9999.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-9999.rst b/pep-9999.rst index 4fb4b6820e7..af71fab2a09 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -100,7 +100,7 @@ Synopsis: '0.0' >>> x = decimal.Decimal('-.00001') - >>> '{}'.format('+z.1f') + >>> '{:+z.1f}'.format(x) '+0.0' >>> x = 0 From 7a8623731eb0b29bc5b890c772d60ed952b502f4 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Fri, 4 Feb 2022 22:47:42 +0900 Subject: [PATCH 05/12] suggested change: Background -> Motivation Co-authored-by: CAM Gerlach --- pep-9999.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-9999.rst b/pep-9999.rst index af71fab2a09..d23518db725 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -24,7 +24,7 @@ zero to be normalized to positive zero. .. _`string format specification`: https://docs.python.org/3/library/string.html#formatstrings -Background +Motivation ========== Here is negative zero: From 6522671da4c16e2528991b6d2f0ba45c7dcdfb80 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Fri, 4 Feb 2022 23:41:48 +0900 Subject: [PATCH 06/12] PR feedback: * PEP assignment * missing email * correctness * remove speculation * wording * backwards compatibility --- .github/CODEOWNERS | 2 +- pep-9999.rst | 59 ++++++++++++++++++++++++---------------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 68221fa2d13..78812ac6cbf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -555,6 +555,7 @@ pep-0677.rst @gvanrossum pep-0678.rst @iritkatriel pep-0679.rst @pablogsal pep-0680.rst @encukou +pep-0682.rst @mdickinson # ... # pep-0754.txt # ... @@ -642,4 +643,3 @@ pep-8016.rst @njsmith @dstufft pep-8100.rst @njsmith # pep-8101.rst # pep-8102.rst -pep-9999.rst @mdickinson diff --git a/pep-9999.rst b/pep-9999.rst index d23518db725..508e1c0a79b 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -1,8 +1,8 @@ -PEP: 9999 +PEP: 682 Title: Format Specifier for Signed Zero Author: John Belmonte -Sponsor: Mark Dickinson -Discussions-To: https://bugs.python.org/issue45995 +Sponsor: Mark Dickinson +Discussions-To: Status: Draft Type: Standards Track Content-Type: text/x-rst @@ -68,14 +68,9 @@ a redundant (and error-prone) pre-rounding of the input: 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 -been nice to have a formatting option for treatment of negative zero -- -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. +What we would like instead is a first-class option to normalize negative +zero, on top of everything else that numerical string formatting already +offers. Specification @@ -88,8 +83,10 @@ An optional, literal ``z`` is added to the [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] -where ``z`` is allowed only for ``float`` and ``Decimal`` types. The new specifier -is supported by both f-strings and ``str.format()``, but not %-formatting. +where ``z`` is allowed only for numerical types. Support for ``z`` is +provided by the ``__format__()`` method of each numeric type, allowing +the specifier to be used in f-strings, built-in ``format()``, and +``str.format()``. The %-formatting style will not support the new option. Synopsis: @@ -141,25 +138,31 @@ 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. - -There is a long-standing decision not to enhance the old %-formatting, so -only f-strings and ``str.format()`` support the new option. +``[sign[z]]``. The default for ``sign`` (``-``) is not widely known or +explicitly written, so this avoids everyone having to learn it just to use +the ``z`` option. + +While f-strings, built-in ``format``, and ``str.format()`` can access the +new option, %-formatting cannot. There is already precedent for not +extending %-formatting with new options, as was the case for the +``,`` option (:pep:`378`). + +To date, there doesn't appear to be other widely-used languages or libraries +providing such a formatting option for negative zero. However, the same +``z`` option syntax and semantics 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.) +.. _`proposed for C++ std::format()`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1496r2.pdf +.. _`feature request`: https://bugs.python.org/issue45995 -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.) +Backwards Compatibility +======================= -.. _`proposed for C++ std::format()`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1496r2.pdf +The new formatting behavior is opt-in, so numerical formatting of existing +programs will not be affected. Reference Implementation From beb801dbf5c2521ac62a3aac396245df86def278 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sat, 5 Feb 2022 00:15:07 +0900 Subject: [PATCH 07/12] correct ordering of Rationale and Specification --- pep-9999.rst | 76 ++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/pep-9999.rst b/pep-9999.rst index 508e1c0a79b..69c4f98dee1 100644 --- a/pep-9999.rst +++ b/pep-9999.rst @@ -73,44 +73,6 @@ zero, on top of everything else that numerical string formatting already offers. -Specification -============= - -An optional, literal ``z`` is added to the -`Format Specification Mini-Language`_ following ``sign``: - -.. code-block:: text - - [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] - -where ``z`` is allowed only for numerical types. Support for ``z`` is -provided by the ``__format__()`` method of each numeric type, allowing -the specifier to be used in f-strings, built-in ``format()``, and -``str.format()``. The %-formatting style will not support the new option. - -Synopsis: - -.. code-block:: pycon - - >>> x = -.00001 - >>> f'{x:z.1f}' - '0.0' - - >>> x = decimal.Decimal('-.00001') - >>> '{:+z.1f}'.format(x) - '+0.0' - - >>> 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 ========= @@ -158,6 +120,44 @@ this PEP was argued without knowledge of the C++ proposal.) .. _`feature request`: https://bugs.python.org/issue45995 +Specification +============= + +An optional, literal ``z`` is added to the +`Format Specification Mini-Language`_ following ``sign``: + +.. code-block:: text + + [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] + +where ``z`` is allowed only for numerical types. Support for ``z`` is +provided by the ``__format__()`` method of each numeric type, allowing +the specifier to be used in f-strings, built-in ``format()``, and +``str.format()``. The %-formatting style will not support the new option. + +Synopsis: + +.. code-block:: pycon + + >>> x = -.00001 + >>> f'{x:z.1f}' + '0.0' + + >>> x = decimal.Decimal('-.00001') + >>> '{:+z.1f}'.format(x) + '+0.0' + + >>> 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 + + Backwards Compatibility ======================= From 8a58924bdabf222a1dac929c0e1bb1ae13bcc47d Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sat, 5 Feb 2022 00:16:35 +0900 Subject: [PATCH 08/12] rename file --- pep-9999.rst => pep-0682.rst | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pep-9999.rst => pep-0682.rst (100%) diff --git a/pep-9999.rst b/pep-0682.rst similarity index 100% rename from pep-9999.rst rename to pep-0682.rst From 8fd0dd7c51256b0944aae7332f66a5aff2022ab5 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sat, 5 Feb 2022 00:28:33 +0900 Subject: [PATCH 09/12] correct type support --- pep-0682.rst | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pep-0682.rst b/pep-0682.rst index 69c4f98dee1..5bdfd59d425 100644 --- a/pep-0682.rst +++ b/pep-0682.rst @@ -130,9 +130,9 @@ An optional, literal ``z`` is added to the [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] -where ``z`` is allowed only for numerical types. Support for ``z`` is -provided by the ``__format__()`` method of each numeric type, allowing -the specifier to be used in f-strings, built-in ``format()``, and +where ``z`` is allowed for numerical types other than integer. Support for +``z`` is provided by the ``__format__()`` method of each numeric type, +allowing the specifier to be used in f-strings, built-in ``format()``, and ``str.format()``. The %-formatting style will not support the new option. Synopsis: @@ -147,14 +147,6 @@ Synopsis: >>> '{:+z.1f}'.format(x) '+0.0' - >>> 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 From 0aadde668feba5dba552d7935b12389f11cf7046 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sat, 5 Feb 2022 09:18:17 +0900 Subject: [PATCH 10/12] Apply suggestions from code review formatting Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- pep-0682.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pep-0682.rst b/pep-0682.rst index 5bdfd59d425..fd8c0343b40 100644 --- a/pep-0682.rst +++ b/pep-0682.rst @@ -14,8 +14,8 @@ Post-History: Abstract ======== -Though ``float`` and ``Decimal`` types can represent `signed zero`_, in many fields -of mathematics, negative zero is surprising or unwanted -- especially +Though ``float`` and ``Decimal`` types can represent `signed zero`_, in many +fields of mathematics, negative zero is surprising or unwanted -- especially 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. @@ -131,7 +131,7 @@ An optional, literal ``z`` is added to the [[fill]align][sign][z][#][0][width][grouping_option][.precision][type] where ``z`` is allowed for numerical types other than integer. Support for -``z`` is provided by the ``__format__()`` method of each numeric type, +``z`` is provided by the ``.__format__()`` method of each numeric type, allowing the specifier to be used in f-strings, built-in ``format()``, and ``str.format()``. The %-formatting style will not support the new option. From 866094b1ebaaf6ac0a3a4d19a32d27c0a353b38d Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Sat, 5 Feb 2022 09:27:37 +0900 Subject: [PATCH 11/12] remove "for what it's worth"; formatting --- pep-0682.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pep-0682.rst b/pep-0682.rst index fd8c0343b40..c258b951707 100644 --- a/pep-0682.rst +++ b/pep-0682.rst @@ -104,8 +104,8 @@ The proposed extension is intentionally ``[sign][z]`` rather than explicitly written, so this avoids everyone having to learn it just to use the ``z`` option. -While f-strings, built-in ``format``, and ``str.format()`` can access the -new option, %-formatting cannot. There is already precedent for not +While f-strings, built-in ``format()``, and ``str.format()`` can access +the new option, %-formatting cannot. There is already precedent for not extending %-formatting with new options, as was the case for the ``,`` option (:pep:`378`). @@ -113,8 +113,8 @@ To date, there doesn't appear to be other widely-used languages or libraries providing such a formatting option for negative zero. However, the same ``z`` option syntax and semantics 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.) +for C++23. (The original `feature request`_ prompting this PEP was argued +without knowledge of the C++ proposal.) .. _`proposed for C++ std::format()`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p1496r2.pdf .. _`feature request`: https://bugs.python.org/issue45995 From e1ae1fa2dd7295bc97ccb59fb40086310974b541 Mon Sep 17 00:00:00 2001 From: John Belmonte Date: Tue, 8 Feb 2022 21:43:58 +0900 Subject: [PATCH 12/12] * fix abstract commas * fix "it's" ambiguity in rationale * add stack overflow links to Motivation (from Mark D.) * move specification support to Design Notes subsection * add How To Teach This (from Mark D.) --- pep-0682.rst | 68 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/pep-0682.rst b/pep-0682.rst index c258b951707..45b2402ef29 100644 --- a/pep-0682.rst +++ b/pep-0682.rst @@ -15,9 +15,9 @@ Abstract ======== Though ``float`` and ``Decimal`` types can represent `signed zero`_, in many -fields of mathematics, negative zero is surprising or unwanted -- especially +fields of mathematics negative zero is surprising or unwanted -- especially in the context of displaying an (often rounded) numerical result. This PEP -proposes an extension to the `string format specification`_, allowing negative +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 @@ -68,16 +68,33 @@ a redundant (and error-prone) pre-rounding of the input: 0.0 0.1 +There is ample evidence that, regardless of the language, programmers are +often looking for a way to suppress negative zero, and landing on a +variety of workarounds (pre-round, post-regex, etc.). A sampling: + +* `How to have negative zero always formatted as positive zero in a + python string?`_ (Python, post-regex) +* `(Iron)Python formatting issue with modulo operator & "negative zero"`_ + (Python, pre-round) +* `Negative sign in case of zero in java`_ (Java, post-regex) +* `Prevent small negative numbers printing as "-0"`_ (Objective-C, custom + number formatter) + What we would like instead is a first-class option to normalize negative zero, on top of everything else that numerical string formatting already offers. +.. _`How to have negative zero always formatted as positive zero in a python string?`: https://stackoverflow.com/questions/11010683/how-to-have-negative-zero-always-formatted-as-positive-zero-in-a-python-string/36604981#36604981 +.. _`(Iron)Python formatting issue with modulo operator & "negative zero"`: https://stackoverflow.com/questions/41564311/ironpython-formatting-issue-with-modulo-operator-negative-zero/41564834#41564834 +.. _`Negative sign in case of zero in java`: https://stackoverflow.com/questions/11929096/negative-sign-in-case-of-zero-in-java +.. _`Prevent small negative numbers printing as "-0"`: https://stackoverflow.com/questions/10969399/prevent-small-negative-numbers-printing-as-0 + Rationale ========= There are use cases where negative zero is unwanted in formatted number -output -- arguably it's the more common case. Expanding the format +output -- arguably, not wanting it is more common. Expanding the format specification is the best way to support this because number formatting already incorporates rounding, and the normalization of negative zero must happen after rounding. @@ -95,20 +112,6 @@ one-dimensional numerical arrays would be complicated by such pre-rounding: """Format a vector (any iterable) using given per-term format string.""" 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]]``. The default for ``sign`` (``-``) is not widely known or -explicitly written, so this avoids everyone having to learn it just to use -the ``z`` option. - -While f-strings, built-in ``format()``, and ``str.format()`` can access -the new option, %-formatting cannot. There is already precedent for not -extending %-formatting with new options, as was the case for the -``,`` option (:pep:`378`). - To date, there doesn't appear to be other widely-used languages or libraries providing such a formatting option for negative zero. However, the same ``z`` option syntax and semantics has been `proposed for C++ std::format()`_. @@ -150,6 +153,23 @@ Synopsis: .. _`Format Specification Mini-Language`: https://docs.python.org/3/library/string.html#format-specification-mini-language +Design Notes +------------ +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]]``. The default for ``sign`` (``-``) is not widely known or +explicitly written, so this avoids everyone having to learn it just to use +the ``z`` option. + +While f-strings, built-in ``format()``, and ``str.format()`` can access +the new option, %-formatting cannot. There is already precedent for not +extending %-formatting with new options, as was the case for the +``,`` option (:pep:`378`). + + Backwards Compatibility ======================= @@ -157,6 +177,20 @@ The new formatting behavior is opt-in, so numerical formatting of existing programs will not be affected. +How to Teach This +================= +A typical introductory Python course will not cover string formatting +in 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 the `z` option on a negative +value that's rounded to zero by the formatting should be enough. For an +independent developer encountering the feature in someone else's code, +reference to the `Format Specification Mini-Language`_ section of the +library reference manual should suffice. + +.. _`Format Specification Mini-Language`: https://docs.python.org/3/library/string.html#format-specification-mini-language + + Reference Implementation ========================