Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/decimal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,11 @@ Decimal objects
Underscores are allowed for grouping, as with integral and floating-point
literals in code.

.. versionchanged:: 3.11
The underscore grouping option in the :ref:`formatting mini-language
<formatspec>` is now supported for :class:`Decimal` objects:
``f"{Decimal(1234567):_}"`` gives ``'1_234_567'``.

Decimal floating point objects share many properties with the other built-in
numeric types such as :class:`float` and :class:`int`. All of the usual math
operations and special methods apply. Likewise, decimal objects can be
Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ New Modules
Improved Modules
================

decimal
-------

* Formatting of :class:`~decimal.Decimal` objects now supports underscores
for grouping, as outlined in :PEP:`515`. For example,
``f"{Decimal(1234567):_}"`` gives ``'1_234_567'``. Previously, only commas
were supported for grouping. (Contributed by Mark Dickinson in
:issue:`45708`.)

fractions
---------

Expand Down
2 changes: 1 addition & 1 deletion Lib/_pydecimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -6154,7 +6154,7 @@ def _convert_for_comparison(self, other, equality_op=False):
(?P<alt>\#)?
(?P<zeropad>0)?
(?P<minimumwidth>(?!0)\d+)?
(?P<thousands_sep>,)?
(?P<thousands_sep>[_,])?
(?:\.(?P<precision>0|(?!0)\d+))?
(?P<type>[eEfFgGn%])?
\Z
Expand Down
34 changes: 33 additions & 1 deletion Lib/test/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,7 @@ def test_formatting(self):
('\x00>10', '1.2345', '\x00\x00\x00\x001.2345'),
('\x00<10', '1.2345', '1.2345\x00\x00\x00\x00'),

# thousands separator
# thousands separator: ','
(',', '1234567', '1,234,567'),
(',', '123456', '123,456'),
(',', '12345', '12,345'),
Expand Down Expand Up @@ -1082,6 +1082,38 @@ def test_formatting(self):
(',e', '123456', '1.23456e+5'),
(',E', '123456', '1.23456E+5'),

# thousands separator: '_'
('_', '1234567', '1_234_567'),
('_', '123456', '123_456'),
('_', '12345', '12_345'),
('_', '1234', '1_234'),
('_', '123', '123'),
('_', '12', '12'),
('_', '1', '1'),
('_', '0', '0'),
('_', '-1234567', '-1_234_567'),
('_', '-123456', '-123_456'),
('7_', '123456', '123_456'),
('8_', '123456', ' 123_456'),
('08_', '123456', '0_123_456'), # special case: extra 0 needed
('+08_', '123456', '+123_456'), # but not if there's a sign
(' 08_', '123456', ' 123_456'),
('08_', '-123456', '-123_456'),
('+09_', '123456', '+0_123_456'),
# ... with fractional part...
('07_', '1234.56', '1_234.56'),
('08_', '1234.56', '1_234.56'),
('09_', '1234.56', '01_234.56'),
('010_', '1234.56', '001_234.56'),
('011_', '1234.56', '0_001_234.56'),
('012_', '1234.56', '0_001_234.56'),
('08_.1f', '1234.5', '01_234.5'),
# no thousands separators in fraction part
('_', '1.23456789', '1.23456789'),
('_%', '123.456789', '12_345.6789%'),
('_e', '123456', '1.23456e+5'),
('_E', '123456', '1.23456E+5'),

# issue 6850
('a=-7.0', '0.12345', 'aaaa0.1'),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Support underscores in :class:`~decimal.Decimal` object formatting: for
example, ``f"{Decimal(1234567):_}"`` now gives ``'1_234_567'``. (Previously,
this gave an "invalid format string" :exc:`ValueError`.)
6 changes: 6 additions & 0 deletions Modules/_decimal/libmpdec/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,12 @@ mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps)
spec->grouping = "\003\003";
cp++;
}
else if (*cp == '_') {
spec->dot = ".";
spec->sep = "_";
spec->grouping = "\003\003";
cp++;
}

/* fraction digits or significant digits */
if (*cp == '.') {
Expand Down