Repository navigation
Improve printf-style formatting docs for non-built-in types #142108
Copy link
Copy link
Open
Labels
docsDocumentation in the Doc dirDocumentation in the Doc dir
Description
Add a description about how Decimal behaves differently with format and % formatting in https://docs.python.org/3/library/string.html#format-specification-mini-language
For __format__, Decimal is formatted with the highest possible precision as it is.
For % formatting, Decimal is converted to float before formatting.
These behaviors have not changed since they were added.
- The Decimal class was added in Python 2.4 (https://docs.python.org/2.4/whatsnew/whatsnew24.html).
- The __format__ method was added in Python 2.6 (https://docs.python.org/2.6/whatsnew/2.6.html).
Compare:
sys.version |
'%f' % 11111111111111111111.0 |
'%f' % Decimal('11111111111111111111.0') |
Decimal('11111111111111111111.0').__format__('f') |
'%e' % 0.0 |
'%e' % Decimal('0.0') |
Decimal('0.0').__format__('e') |
'%.20e' % 11111111111111111111.0 |
'%.20e' % Decimal('11111111111111111111.0') |
Decimal('11111111111111111111.0').__format__('.20e') |
'%.20g' % 11111111111111111111.0 |
'%.20g' % Decimal('11111111111111111111.0') |
Decimal('11111111111111111111.0').__format__('.20g') |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| '2.4.4 (#71, Oct 18 2006, 08:34:43) [MSC v.1310 32 bit (Intel)]' | '11111111111111111000.000000' | '11111111111111111000.000000' | Exception | '0.000000e+000' | '0.000000e+000' | Exception | '1.11111111111111110000e+019' | '1.11111111111111110000e+019' | Exception | '11111111111111111000' | '11111111111111111000' | Exception |
| '2.6.6 (r266:84297, Aug 24 2010, 18:13:38) [MSC v.1500 64 bit (AMD64)]' | '11111111111111111000.000000' | '11111111111111111000.000000' | '11111111111111111111.0' | '0.000000e+00' | '0.000000e+00' | '0e-1' | '1.11111111111111110000e+19' | '1.11111111111111110000e+19' | '1.11111111111111111110e+19' | '11111111111111111000' | '11111111111111111000' | '11111111111111111111' |
| '2.7.18 (v2.7.18:8d21aa21f2, Apr 20 2020, 13:25:05) [MSC v.1500 64 bit (AMD64)]' | '11111111111111110656.000000' | '11111111111111110656.000000' | '11111111111111111111.0' | '0.000000e+00' | '0.000000e+00' | '0e-1' | '1.11111111111111106560e+19' | '1.11111111111111106560e+19' | '1.11111111111111111110e+19' | '11111111111111110656' | '11111111111111110656' | '11111111111111111111' |
| '3.14.0 (tags/v3.14.0:ebf955d, Oct 7 2025, 10:06:33) [MSC v.1944 32 bit (Intel)]' | '11111111111111110656.000000' | '11111111111111110656.000000' | '11111111111111111111.0' | '0.000000e+00' | '0.000000e+00' | '0e-1' | '1.11111111111111106560e+19' | '1.11111111111111106560e+19' | '1.11111111111111111110e+19' | '11111111111111110656' | '11111111111111110656' | '11111111111111111111' |
| '3.14.0 (tags/v3.14.0:ebf955d, Oct 7 2025, 10:15:03) [MSC v.1944 64 bit (AMD64)]' | '11111111111111110656.000000' | '11111111111111110656.000000' | '11111111111111111111.0' | '0.000000e+00' | '0.000000e+00' | '0e-1' | '1.11111111111111106560e+19' | '1.11111111111111106560e+19' | '1.11111111111111111110e+19' | '11111111111111110656' | '11111111111111110656' | '11111111111111111111' |
| '3.15.0a2 (tags/v3.15.0a2:a625628, Nov 18 2025, 18:12:01) [MSC v.1944 64 bit (AMD64)]' | '11111111111111110656.000000' | '11111111111111110656.000000' | '11111111111111111111.0' | '0.000000e+00' | '0.000000e+00' | '0e-1' | '1.11111111111111106560e+19' | '1.11111111111111106560e+19' | '1.11111111111111111110e+19' | '11111111111111110656' | '11111111111111110656' | '11111111111111111111' |
# ./test.py
import sys
from decimal import Decimal
for expr in (
"sys.version",
"'%f' % 12345123451234512345.0",
"'%f' % Decimal('12345123451234512345.0')",
"Decimal('12345123451234512345.0').__format__('f')",
"'%e' % 0.0",
"'%e' % Decimal('0.0')",
"Decimal('0.0').__format__('e')",
"'%.20e' % 12345123451234512345.0",
"'%.20e' % Decimal('12345123451234512345.0')",
"Decimal('12345123451234512345.0').__format__('.20e')",
"'%.20g' % 12345123451234512345.0",
"'%.20g' % Decimal('12345123451234512345.0')",
"Decimal('12345123451234512345.0').__format__('.20g')",
):
print(expr)
try:
print(repr(eval(expr)))
except Exception:
print('Exception')
print('')# ./test_runner.py
from pathlib import Path
from subprocess import run, PIPE
PYTHON_DIR_ROOT = r"C:\python_dir_root"
def print_row(row, file):
for v in row: print(f"| {v} ", end='', file=file)
print("|", file=file)
def print_md_table(headers, rows, file):
print_row(headers, file=file)
print_row(['---:'] * len(headers), file=file)
for row in rows: print_row(row, file=file)
print(file=file)
rows = []
for dir in Path(PYTHON_DIR_ROOT).iterdir():
out = run([str(dir/"python.exe"), "./test.py"], stdout=PIPE).stdout
out = out.decode('utf-8').splitlines()
headers = [f"`{h}`" for h in out[::3]]
rows.append(out[1::3])
with open("./test_result.md", 'w', encoding='utf-8') as fp:
print_md_table(headers, rows, file=fp)Linked PRs
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
docsDocumentation in the Doc dirDocumentation in the Doc dir
Projects
- StatusShow more project fieldsTodo
Activity
skirpichev commentedon Nov 30, 2025
It's just not true. Only when precision is omitted in f/e/g formats. This is documented.
This is not specific to Decimal's at all:
Old-style formatting is documented here: https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
Maybe rules are obvious, yet they aren't documented. N.B.: implementation is in the Objects/unicode_format.c
Yashp002 commentedon Nov 30, 2025
I'd like to work on this. I'll add clarification about Decimal's formatting behavior to the format specification docs.
picnixz commentedon Nov 30, 2025
When we have a
pendinglabel, usually this means that we still don't know whether this is triaged. AFAICT, what the OP described as an issue was not an issue. IMO, there is nothing to do (unless @skirpichev had something precise in mind?)skirpichev commentedon Nov 30, 2025
Sorry, perhaps I wasn't clear.
Documentation for printf-style formatting lacks description of rules to format non-builtin types. @picnixz, if such rules are obvious for you - perhaps there is no issue.
picnixz commentedon Nov 30, 2025
Oh if for non-built-in types, we don't document
%-format, then yes I'm fine with documenting them. But I don't really where we would put them. I think we can amend theprintf-style sections forstrandbytesto say something like:The above sentence is clearly not perfect and I would rather have something equivalent and better but that convenes this intent (that is, we have an implicit conversion).
[-]Add a description about how Decimal behaves differently with __format__ and % formatting[/-][+]Improve `printf`-style formatting for non-built-in types[/+][-]Improve `printf`-style formatting for non-built-in types[/-][+]Improve `printf`-style formatting docs for non-built-in types[/+]skirpichev commentedon Nov 30, 2025
We have a dedicated section for
%-formatting: https://docs.python.org/3/library/stdtypes.html#printf-style-string-formattingYes, something like that. Though, logic is much more complex, e.g. for integer formatting types:
cpython/Objects/unicode_format.c
Lines 297 to 317 in 229ed3d
(For floats it's more close to
float(obj)for non-stringobj's.)picnixz commentedon Nov 30, 2025
For
%o& co, it's actually documented inobject.__index__but it's honestly better if we were to indicate this in the printf-style section. So I'm in favor of having something that really explains the logic at the same place (I guess the entire thing is documented but probably in differnt places which makes it very hard to search)1nftf commentedon Nov 30, 2025
I discovered the formatting difference while investigating #142019
I found this document first:
https://docs.python.org/3/library/string.html#format-specification-mini-language.
When I tried to find another document, I searched for '% format'
and '% formatting' but found nothing related to
https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting.
I misunderstood that there is only one document about formatting,
and that the basic functionalities of the format-specification mini-language are also applicable to % formatting,
except for some additional functions.
Perhaps a note could be added at the beginning of
https://docs.python.org/3/library/string.html#format-specification-mini-language
stating that this is not suitable for % formatting, along with a link back to
https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting
1nftf commentedon Nov 30, 2025
I'm focusing on #142019, so I can't pay much attention to this currently.
skirpichev commentedon Nov 30, 2025
Not, unless I misread it:
But you are right, in sense that we could reference to this logic in formatting docs.
I don't think we need this.
1nftf commentedon Nov 30, 2025
Why? Because this old formatting syntax is not encouraged to be used?
picnixz commentedon Nov 30, 2025
Format specifications and printf-style are different. One is for
fmt % value, the other is forformat()orstr.format.picnixz commentedon Nov 30, 2025
I don't think this is issue is very newcomer friendly actually. It requires quite a deep knowledge in the subtle terms as well as knowledge in the implementation. I would suggest either @skirpichev or me to document this but I don't have much time.
It's not explicitly stated, but I would have assumed that mention to
bin/hex/octcombined with "whenever Python needs to losslessly convert the numeric object to an integer object" actually covered this. Actually theoctdocs say:So I guess we could say it's documented, though maybe a bit hard to find.
skirpichev commentedon Nov 30, 2025
Because we can't clutter docs with notes each time someone misread them in some way.
I think it's a good opportunity to learn. There is an open pr, lets see if @Yashp002 will read discussion thread and address review comments.
If not, I'll try to finish this.
BTW,
Side story. Should we promote old-style formatting in usual examples? I don't think so.
1nftf commentedon Nov 30, 2025
I believe there should at least be a link to #printf-style-string-formatting in #format-specification-mini-language.Or maybe replace the literal %-formatting in #format-examples by a link
I didn't realize #printf-style-string-formatting exists, until @skirpichev mentioned it.
The built-in document search engine is basically useless when it comes to searching for symbols and multiple keywords.
picnixz commentedon Nov 30, 2025
I guess it doesn't hurt to also backreference
%-style informat-style with a single sentence at the end of the first paragraph. "For old-style formatting, see [...]".Personally, I use old-style formatting because it's easier to me to read. There is a real visual benefit IMO where I can more easily parse
%tokens rather than{varname}. So while I understand that people don't always like old-style formatting I do use it myself if needed (it's especially useful for double-formatting;(fmt % args).format(after), which happens when you want to pre-compute the width of your field (it's possible to do it with f-strings but it's unreadable to me honestly).skirpichev commentedon Nov 30, 2025
Seems fine.
It depends on how you read the docs. Starting from the Tutorial here you will quickly navigate to both pages.
Yashp002 commentedon Dec 1, 2025
Hi, I attempted a PR but realized after review that I may have misunderstood the scope of this issue.
The issue title mentions "printf-style formatting" (% operator) but the linked doc section is about the Format Specification Mini-Language (format / f-strings / .format()).
Could you clarify:
pythongh-142108: document conversion rules for printf-style formatting