How can I format a Decimal
using a locale?
To describe by examples, I'm trying to define a function f
such that in the US English locale:
f(Decimal('5000.00')) == '5,000.00'
f(Decimal('1234567.000000')) == '1,234,567.000000'
Some things that don't work:
f = str
doesn't use locale; f(Decimal('5000.00')) == '5000.00'
f = lambda d: locale.format('%f', d)
doesn't preserve the decimal precision; f(Decimal('5000.00')) == '5000.000000'
f = lambda d: locale.format('%.2f', d)
uses a fixed precision, which isn't what I'm after; f(Decimal('1234567.000000')) == '1234567.00'
To format decimals, we will use str. format(number) where a string is '{0:. 3g}' and it will format string with a number. Also, it will display the number with 1 number before the decimal and up to 2 numbers after the decimal.
Use str. format() with “{:. 2f}” as string and float as a number to display 2 decimal places in Python. Call print and it will display the float with 2 decimal places in the console.
Reading through the source for the decimal
module, Decimal.__format__
provides full PEP 3101 support, and all you have to do is select the correct presentation type. In this case, you want the :n
type. According PEP 3101 spec, this :n
has the following properties:
'n' - Number. This is the same as 'g', except that it uses the current locale setting to insert the appropriate number separator characters.
This is simpler than other answers, and avoids the float precision issue in my original answer (preserved below):
>>> import locale
>>> from decimal import Decimal
>>>
>>> def f(d):
... return '{0:n}'.format(d)
...
>>>
>>> locale.setlocale(locale.LC_ALL, 'en_us')
'en_us'
>>> print f(Decimal('5000.00'))
5,000.00
>>> print f(Decimal('1234567.000000'))
1,234,567.000000
>>> print f(Decimal('123456700000000.123'))
123,456,700,000,000.123
>>> locale.setlocale(locale.LC_ALL, 'no_no')
'no_no'
>>> print f(Decimal('5000.00'))
5.000,00
>>> print f(Decimal('1234567.000000'))
1.234.567,000000
>>> print f(Decimal('123456700000000.123'))
123.456.700.000.000,123
You can just tell the format string to use as much precision as is included in the decimal itself and use the locale formatter:
def locale_format(d):
return locale.format('%%0.%df' % (-d.as_tuple().exponent), d, grouping=True)
Note that works if you've got a decimal which corresponds to a real number, but doesn't work correctly if the decimal is NaN
or +Inf
or something like that. If those are possibilities in your input, you'd need to account for them in the format method.
>>> locale.setlocale(locale.LC_ALL, 'en_US')
'en_US'
>>> locale_format(Decimal('1234567.000000'))
'1,234,567.000000'
>>> locale_format(Decimal('5000.00'))
'5,000.00'
>>> locale.setlocale(locale.LC_ALL, 'no_no')
'no_no'
>>> locale_format(Decimal('1234567.000000'))
'1.234.567,000000'
>>> locale_format(Decimal('5000.00'))
'5.000,00'
>>> locale_format(Decimal('NaN'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in locale_format
TypeError: bad operand type for unary -: 'str'
A modern Python approach with Python 3.5+ would be:
>>> import locale
>>> locale.setlocale(locale.LC_NUMERIC, "de_DE")
>>> f'{Decimal("5000.00"):n}'
'5.000,00'
>>> locale.setlocale(locale.LC_NUMERIC, "en_US")
'en_US'
>>> f'{Decimal("5000.00"):n}'
'5,000.00'
F-strings support advanced formatting. Hence, there is no need for extra f
function. Babel for Python will give you a bit more convenience:
>>> from babel import numbers
>>> numbers.format_decimal(.2345, locale='en_US')
'0.234'
>>> numbers.format_decimal(.2345, locale='de_DE')
'0,234'
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With