2

There are many well established answers for how to print numbers with commas and/or decimal places, examples here, here, here, etc.

For some reason I cannot find a clear answer on how to do this for large numbers with decimals in a locale-aware way that does not force me to hard-code in the precision (e.g. when reading a file and printing values from it, no prior knowledge of how large or precise the numbers will be).

To make things more complicated I am also hoping to do this in a Python 2.7/3.x cross-compatible way.

So, if I had a number such as 1000000.01, I would want it printed as "1,000,000.01", and 1000000.00001 as "1,000,000.00001".

Here are methods I have tried which do not work:

(using the standard string formatter)

# https://docs.python.org/2.7/library/string.html#format-specification-mini-language
x = 1000000.01
print("{:n}".format(x)) # 1e+06 ; locale-aware but doesn't work?
print("{:g}".format(x)) # 1e+06 ; doesn't work
print("{:f}".format(x)) # 1000000.010000 ; wrong number of decimal places
print("{:,}".format(x)) # 1,000,000.01 ; desired result but not locale-aware (?)

based on the docs, "{:n}" sounds like what I want but does not seem to actually work. "{:,}" gives the desired result but does not seem to be locale-aware (as per the docs listed; need clarification on this).

(using locale library)

# https://docs.python.org/2/library/locale.html
import locale
locale.setlocale(locale.LC_ALL, '')
print(locale.format("%g", x, grouping = True)) # 1e+06 ; doesn't work
print(locale.format("%d", x, grouping = True)) # 1,000,000 ; loses decimal place
# print(locale.format("%n", x, grouping = True)) # ValueError: format() must be given exactly one %char format specifier, '%n' not valid
# print(locale.format("{:n}", x, grouping = True)) # ValueError: format() must be given exactly one %char format specifier, '{:n}' not valid

print(locale.format_string("%g", x, grouping = True)) # 1e+06 ; doesn't work
# print(locale.format_string("%n", x, grouping = True)) # TypeError: not enough arguments for format string
print(locale.format_string("{:n}", x, grouping = True)) # {:n} ; not a valid formatter ?

It seems like locale.format("%g", ...) is closest to what I want but it does not appear to work correctly:

print(locale.format("%g", 1000.01, grouping = True)) # 1,000.01 ; works but only for small numbers
print(locale.format("%g", 10000.01, grouping = True)) # 10,000 ; doesn't work, loses decimal

It also seems like the n formatter is missing from local.format.

Any ideas? Seems really weird that there is no common established method for this, that I've found so far at least.

user5359531
  • 3,217
  • 6
  • 30
  • 55

1 Answers1

0

I mean, you could split at the decimal point and compute separately. It might not be ideal but it works.

# `input` is your number, `output` is the formatted string.
import locale #import
sep = '.' if locale.localeconv()['thousands_sep'] == ',' else ',' #get locale sep
parts = str(input).split(sep) #get part before and after the sep
parts[0] = locale.format("%d", parts[0], grouping=True) #format **part 0**
output = sep.join(parts) #rejoin

I think this will do it.

The main part of importance here is the call to locale.localeconv()['thousands_sep'], which gets the value separator. Then, you invert it on the same line, so that a comma becomes a period and vice verca.

Eb946207
  • 748
  • 8
  • 26
  • I may be missing something again, but plain `print("{:,}".format(x))` works for me without any more fussing required... – Julien Jan 02 '19 at 23:15
  • @Julien Are you sure? I haven't tested so I could be wrong, but I believe this is right. – Eb946207 Jan 02 '19 at 23:15
  • **Never mind, I re-read the question and I understand now! I edited my answer to work!** *(I think... I still can't test)* – Eb946207 Jan 02 '19 at 23:28
  • @user5359531 input is your non-formatted number, and output is the formatted string. – Eb946207 Jan 03 '19 at 00:39