turnip
turnip

Reputation: 2346

Weird arithmetic/rounding behaviour in Python

This little thing has been driving me crazy for the past couple of hours.

Here is my code. I noticed the problem when I used int() to round down some numbers. You can see I am printing the float and integer answers side by side for comparison.

class Payment:
def __init__(self):
    self.coins = [0.25, 0.5, 1, 3, 15, 75]

def get_change(self, price, payment):
    change = (payment - price) * 100.0
    self.calculate_change(change)

def calculate_change(self, change):
    for coin in self.coins:
        print change / coin, int(change / coin)

And in my Main I have:

from Temp import Payment

t = Payment()
t.get_change(2.0, 2.80)

So what's the problem you wonder?

From the Main input the value of change happens to be 80 and is passed into calculate_change. Now, have a look at the output

320.0 319
160.0 159
80.0 79
26.6666666667 26
5.33333333333 5
1.06666666667 1

The rounded values are off by 1. That's odd.

But if I just call the calculate_changefunction in Main like so:

t.calculate_change(80)

I get what is expected:

320.0 320
160.0 160
80 80
26 26
5 5
1 1

So the problem lies somewhere in the get_change function. What is it about this line that causes the problem?

change = (payment - price) * 100.0

Upvotes: 0

Views: 583

Answers (1)

Martin Konecny
Martin Konecny

Reputation: 59671

The problem is that floating point numbers are never represented exactly.

The calculation change = (payment - price) * 100.0 will not give you 80 exactly but 79.9999999 or 80.00000001.

When you use int on the former, you are just chopping off everything after the decimal and getting 79.

The solution is to never use inaccurate data types such as float to represent money. Use whole numbers, so instead of everything in dollars like 2.80, use cents 280. Only when you are ready to present the value to the user do you convert to cents to a dollar representation.

Alternatively, you could use something like Python's decimal class, which can represent decimal numbers with a much higher accuracy at the cost of speed, and a little extra code required each time you work with a number.

Upvotes: 1

Related Questions