There are a few existing questions about float formatting, but none answer the following question, I think.
I'm looking for a way to print large floats in a long, nicely rounded and localized format:
>>> print magic_format(1.234e22, locale="en_US")
12,340,000,000,000,000,000,000
>>> print magic_format(1.234e22, locale="fr_FR")
12 340 000 000 000 000 000 000
Unfortunately, magic_format
does not exist. ;-) How can I implement it?
Details
Here are a few ways to print floats. None of them produces the above output:
>>> x = 1.234e22
>>> print str(x)
1.234e+22
>>> print repr(x)
1.234e+22
>>> print "%f" % x
12339999999999998951424.000000
>>> print "%g" % x
1.234e+22
Fail: either I get the short version, or a non-grouping non-localized non-rounded output.
BTW, I understand that 1.234e22 cannot be stored exactly as a float, there's a necessary rounding error (that explains the odd output above). But since str
, repr
and "%g" % x
are able to properly round that to the proper value, I would like to have the same friendly rounded number, but in a long and localized form.
Let's try localizing now...
>>> import locale
>>> locale.setlocale(locale.LC_ALL, "en_US")
'en_US'
>>> locale.format("%g", x, grouping = True)
'1.234e+22'
>>> locale.format("%f", x, grouping = True)
'12,339,999,999,999,998,951,424.000000'
>>> locale.setlocale(locale.LC_ALL, "fr_FR")
'fr_FR'
>>> locale.format("%g", x, grouping = True)
'1,234e+22'
>>> locale.format("%f", x, grouping = True)
'12339999999999998951424,000000'
Closer, but not ok. I still have the annoying rounding error, and the French localization sucks, it does not allow grouping at all.
So let's use the excellent Babel library, perhaps it can do everything I want:
>>> from babel.numbers import format_number
>>> format_number(x, locale = "en_US")
u'12,339,999,999,999,998,951,424'
>>> format_number(x, locale = "fr_FR")
u'12\xa0339\xa0999\xa0999\xa0999\xa0998\xa0951\xa0424'
Wow, really close. They even use non-breakable spaces for grouping in French, I love it. It's really too bad they still have the rounding issue.
Hey!? What if I used python Decimals?
>>> from decimal import Decimal
>>> Decimal(x)
Decimal('12339999999999998951424')
>>> Decimal("%g" % x)
Decimal('1.234E+22')
>>> "%g" % Decimal("%g" % x)
'1.234e+22'
>>> "%f" % Decimal("%g" % x)
'12339999999999998951424.000000'
Nope. I can get an exact representation of the number I want with Decimal("%g" % x)
, but whenever I try to display it, it's either short or converted to a bad float before it's printed.
But what if I mixed Babel and Decimals?
>>> Decimal("%g" % 1.234e22)
Decimal('1.234E+22')
>>> dx = _
>>> format_number(dx, locale = "en_US")
Traceback (most recent call last):
...
TypeError: bad operand type for abs(): 'str'
Ouch. But Babel's got a function called format_decimal
, let's use that instead:
>>> from babel.numbers import format_decimal
>>> format_decimal(dx, locale = "en_US")
Traceback (most recent call last):
...
TypeError: bad operand type for abs(): 'str'
Oops, format_decimal
can't format python Decimals. :-(
Ok, one last idea: I could try converting to a long
.
>>> x = 1.234e22
>>> long(x)
12339999999999998951424L
>>> long(Decimal(x))
12339999999999998951424L
>>> long(Decimal("%g" % x))
12340000000000000000000L
Yes! I've got the exact number I want to format. Let's give that to Babel:
>>> format_number(long(Decimal("%g" % x)), locale = "en_US")
u'12,339,999,999,999,998,951,424'
Oh, no... Apparently Babel converts the long
to a float
before trying to format it. I'm out of luck, and out of ideas. :-(
If you think that this is tough, then try answering the same question for x = 1.234e-22
. So far all I can print is either the short form 1.234e-22
or 0.0
!
I would prefer this:
>>> print magic_format(1.234e-22, locale="en_US")
0.0000000000000000000001234
>>> print magic_format(1.234e-22, locale="fr_FR")
0,0000000000000000000001234
>>> print magic_format(1.234e-22, locale="en_US", group_frac=True)
0.000,000,000,000,000,000,000,123,400
>>> print magic_format(1.234e-22, locale="fr_FR", group_frac=True)
0,000 000 000 000 000 000 000 123 400
I can imagine writing a little function that would parse "1.234e-22"
and format it nicely, but I would have to know all about the rules of number localization, and I'd rather not reinvent the wheel, Babel is supposed to do that. What should I do?
Thanks for your help. :-)