18

I am writing a class to represent money, and one issue I've been running into is that "1.50" != str(1.50). str(1.50) equals 1.5, and alll of a sudden, POOF. 45 cents have vanished and the amount is now 1 dollar and 5 cents. not one dollar and 50 cents. Any way I could prevent str from doing this, or am I doing something wrong? This is Python 2 BTW.

Johm Don
  • 609
  • 2
  • 5
  • 15
  • 1
    This illustrates an important point: one dollar and fifty cents is **not** equivalent to, say, 1.5 pounds of weight. 1.5 pounds and 1.50 pounds are exactly the same thing, and you rarely care about the difference between 1.5 pounds and 1.50001 pounds. This is because "pounds" is measuring a *continuous* quantity (weight), while "cents" is measuring a *discrete* quantity. – Daniel Pryden Mar 06 '13 at 03:07
  • 1
    Bad class design strikes again... –  Nov 09 '15 at 03:38
  • wait...why is `1.5` a dollar and five cents? – Code-Apprentice Nov 21 '22 at 04:20

4 Answers4

30

You can use the format method on strings to specify how many decimal places you want to represent:

>>> "{:.2f}".format(1.5)
'1.50'

But even better would be to use the decimal module for representing money, since representation issues with binary floats can give you slightly off results if you're doing arithmetic. The documentation for that module mentions some of those issues specifically - one of the most interesting ones for money applications is:

>>> 0.1+0.1+0.1-0.3
5.551115123125783e-17
>>> from decimal import Decimal
>>> Decimal('.1') + Decimal('.1') + Decimal('.1') - Decimal('.3')
Decimal('0.0')
lvc
  • 34,233
  • 10
  • 73
  • 98
12

When working with money, always represent money using the Decimal class.

http://docs.python.org/2/library/decimal.html

Wei Yen
  • 350
  • 3
  • 10
12

The proposed solutions do not work when the magnitude of the number is not known in advance, which is common in scientific applications rather than in money-related ones. I give an alternative solution for those, like me, coming to this question looking for the scientific case.

For example, if I want to print x = 1.500e-4 to three significant digits (common situation when dealing with measurements with a given uncertainty), the following command obviously does not give the correct result:

x = 1.500e-4
print(f"{x:.3f}")

----> 0.000

Here I used the modern Python 3.6+ f-strings for the formatting.

One may think of using the g format specifier, but this also does not give the desired result to three significant digits as the trailing zero is omitted:

x = 1.500e-4
print(f"{x:.3g}")

----> 0.00015

The correct answer can be obtained using the g format specifier together with a rather obscure option, the hash character #, of the format-specification mini language, in the following way:

x = 1.500e-4
print(f"{x:#.3g}")

----> 0.000150

This formatting also works unchanged in the simpler case of the original question:

x = 1.500
print(f"{x:#.3g}")

----> 1.50
divenex
  • 15,176
  • 9
  • 55
  • 55
  • 2
    This is *exactly* what I was looking for--thanks also for linking to the mini language docs, excellent resource. – sudo make install Feb 27 '22 at 20:58
  • Very nice. This uses the *alternate form* as described also [here](https://python-reference.readthedocs.io/en/latest/docs/str/formatting.html). – Benjamin Wang Sep 20 '22 at 13:34
3
x = 1.500000

print '%.2f' % x
print '{:.3f}'.format(x)

result

1.50
1.500
eyquem
  • 26,771
  • 7
  • 38
  • 46