classification
Title: Behavior of general (%g, :g) formatting inconsistent for decimal.Decimal
Type: behavior Stage:
Components: Documentation Versions: Python 3.10, Python 3.9, Python 3.8
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: Kwpolska, docs@python, eric.smith, mark.dickinson
Priority: normal Keywords:

Created on 2020-11-21 23:29 by Kwpolska, last changed 2020-11-23 18:31 by mark.dickinson.

Messages (6)
msg381578 - (view) Author: Chris Warrick (Kwpolska) Date: 2020-11-21 23:29
When formatting decimal.Decimal using old-style formatting (%g), the output is as short as possible, as expected. When using new-style formatting (str.format or f-strings), the output uses the input precision. Floats behave correctly with new-style formatting. 

Python 3.9.0 (default, Oct 27 2020, 14:15:17)
[Clang 12.0.0 (clang-1200.0.32.21)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import decimal
>>> d1 = decimal.Decimal("1.000")
>>> d2 = decimal.Decimal("1.500")
>>> f1 = 1.0
>>> f2 = 1.5
>>> f"{d1:g} {f1:g}"
'1.000 1'
>>> f"{d2:g} {f2:g}"
'1.500 1.5'
>>> "%g %g" % (d1, f1)
'1 1'
>>> "%g %g" % (d2, f2)
'1.5 1.5'
msg381587 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2020-11-22 03:28
A few things:

- There is no %-formatting for Decimal types. They're being converted to float first. That's why it appears that %-formatting with 'g' works the same for decimal and float: you're really just calling the float version.

- The difference in 'g' formatting between float and Decimal is a known difference. This issue pops up every now and again, but right now I can't find where (or if) it's actually documented.

Mark: can you point to it? I don't think https://docs.python.org/3/library/string.html#format-specification-mini-language mentions it where it talks about Decimals. And I don't see any documentation for Decimal.__format__.
msg381666 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-11-23 12:56
Hmm. I'm not sure that it ever got documented properly. There may still be an open documentation issue somewhere.

There's not really much wiggle-room for changing the implementation: the behaviour of the "g" formatting for Decimal objects is following the specification of "to-scientific-string" from the Decimal Arithmetic spec that the decimal module is based on: http://speleotrove.com/decimal/daconvs.html#reftostr

One of the principles articulated there is that to-scientific-string should be faithful, so that conversion to string and back doesn't lose any information. That precludes chopping significant trailing zeros.
msg381682 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-11-23 17:24
Some references after a bit of tracker digging:

- #7098 is the original issue where the behaviour was considered.
- #23460 is related, and _did_ result in a documentation change, but that documentation change didn't say anything about preserving trailing zeros
- #13433 is relevant, and still open

So I think we're still missing a documentation update.
msg381684 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-11-23 17:55
There's also #39096.
msg381686 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2020-11-23 18:31
Here's a first draft of proposed re-wording for the 'e', 'f' and 'g' sections of the table in the general format-specification mini-language docs. (I started making a PR, but got too annoyed with the mechanics of editing reStructuredText tables.)


'e': Scientific "E" notation. For a given precision ``p >= 0``, formats the
number in scientific notation with the letter 'e' separating the coefficient
from the exponent. The coefficient has one digit before and ``p`` digits after
the decimal point, for a total of ``p + 1`` significant digits. With no
precision, uses a precision of ``6`` digits after the decimal point for
:class:`float`, and shows all coefficient digits (including any trailing zeros)
for :class:`~decimal.Decimal`.

'f': Fixed-point notation. For a given precision ``p >= 0``, formats the number
as a decimal number with exactly ``p`` digits following the decimal point. With
no precision, uses a precision of ``6`` digits after the decimal point for
:class:`float`, and shows all coefficient digits (including any trailing zeros)
for :class:`~decimal.Decimal`. Note that in the case of a
:class:`~decimal.Decimal` instance with a positive exponent, the formatted
output will consist of the digits of the coefficient sequence extended by
additional zeros: for example, ``format(Decimal('1.23e4'), 'f')`` gives
``'12300'``.

'g': <text as before, up to but not including the last paragraph, then:>

A precision of ``0`` is treated as equivalent to a precision of ``1``.
With no precision, uses a precision of ``6`` significant digits for
:class:`float`, and shows all coefficient digits (including any trailing zeros)
for :class:`~decimal.Decimal`.
History
Date User Action Args
2020-11-23 18:31:45mark.dickinsonsetmessages: + msg381686
2020-11-23 17:55:21mark.dickinsonsetmessages: + msg381684
2020-11-23 17:24:07mark.dickinsonsetversions: + Python 3.8, Python 3.10
nosy: + docs@python

messages: + msg381682

assignee: docs@python
components: + Documentation, - Library (Lib)
2020-11-23 12:56:33mark.dickinsonsetmessages: + msg381666
2020-11-22 03:28:06eric.smithsetnosy: + mark.dickinson, eric.smith
messages: + msg381587
2020-11-21 23:29:40Kwpolskacreate