NeoWang
NeoWang

Reputation: 18513

Is float field + round() for currency fields in django OK?

I was naive to use float field for currency in my django project. I just learned about this rounding problem with python float:

>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17

Now I have 2 options:

  1. Migrate all float fields to decimal fields with South, this is a lot of pain.
  2. Do a round() after each addition and subtraction to get the correct result before saving. After all, all the currency fields in my app only allows 1 decimal place.

My question is, is option 2 OK if all I am dealing with is adding and subtracting numbers like 100.0 and 1.2? Is there anything else to worry about aside from the rounding errors?

Upvotes: 0

Views: 647

Answers (3)

korvus
korvus

Reputation: 395

To be clear, the problem isn't with Python's float, but with a binary representation of floating point numbers -- it often isn't exact for numbers that are easy to represent in decimal.

In general, I'd agree with the other answers that you should use the Decimal class, as that is designed for exact representation of floating point numbers (at the cost of space + efficiency of mathematical operations). The other option, if you know exactly what your precision will be (as you said, once decimal point) would be to use integers as fixed point numbers. You'd store 1.3 as 13 and just know you need to shift it by one decimal place before displaying. But using Decimal is cleaner and the code is likely to be more readable.

Upvotes: 1

HarmonicaMuse
HarmonicaMuse

Reputation: 7893

Use decimal fields, which use decimal.Decimal, for handling currency accurately in Python:

>>> from decimal import Decimal as D
>>> D('0.1') + D('0.2') - D('0.3')
Decimal('0.0')
>>> assert _ == 0

Upvotes: 1

Burhan Khalid
Burhan Khalid

Reputation: 174624

Be careful with round(); round(0.1+0.2-0.3+1.2) will give you 1.0, not 1.2. You need to use Decimal, then convert it correctly to float:

>>> r = Decimal('0.1') + Decimal('0.2') - Decimal('0.3')
>>> r
Decimal('0.0')
>>> float(r)
0.0
>>> round(0.1+0.2-0.3+1.2)
1.0
>>> r = Decimal('0.1') + Decimal('0.2') - Decimal('0.3') + Decimal('1.2')
>>> r
Decimal('1.2')
>>> float(r)
1.2

This will also take care of your edge cases which you will have to deal with manually should go with the float() + round() approach.

Finally, do not confuse how the value is printed with how its stored. "only allows 1 decimal place" - this is a presentation detail.

Upvotes: 1

Related Questions