# Python math issue

Discussion in 'Mac Programming' started by mysterytramp, Jun 12, 2009.

1. ### mysterytramp macrumors 65816

Joined:
Jul 17, 2008
Location:
Maryland
#1
I'm doing an exercise in "Core Python Programming" where you calculate the coins necessary to make an amount of money. This works most of the time:

Code:
```coins = [0,0,0,0,0,0]
# dollars, half dollars, quarters, dimes, nickels, pennies
values = [100,50,25,10,5,1]

amountString = raw_input('Enter amount: ')

amount = 100*float(amountString)

for x in range(0,6,1):
coins[x] = amount//values[x]
amount %= values[x]

print "Dollars: ", coins[0]
print "Half Dollars: ", coins[1]
print "Quarters: ", coins[2]
print "Dimes: ", coins[3]
print "Nickels: ", coins[4]
print "Pennies: ", coins[5]
```
but occasionally I get weird answers. 5.10 is five dollars, a nickel and four pennies. 4.56 is four dollars, a half dollar and a nickel. Weird

Python 2.5.4 on OS X 10.4.11

mt

2. ### wrldwzrd89 macrumors G5

Joined:
Jun 6, 2003
Location:
Solon, OH
#2
This is almost certainly caused by floating point representation errors. These can be avoided, if you're careful with exactly how you process the input.

The easiest way to deal with this problem is to use the round() function to chop the decimal to 2 digits after the decimal, before working with it. Here's an example of how it's used:

Code:
```money = raw_input('Enter amount: ')
money = round(money, 2) # Chop money variable to last 2 digits after decimal
```

3. ### lee1210 macrumors 68040

Joined:
Jan 10, 2005
Location:
Dallas, TX
#3
I said this in another post recently, but it bears repeating. Almost every situation that is dealing with money calls for fixed point math on the cents.
I would get your input as a string, split it on the . then add 100*the first portion to the second portion as integers. Now you have a number of whole cents (if the 2nd part is more than 2 characters, throw out an error). You can then do exact integer math using each denomination's value in cents.

Floating point math in base 10 is inherently inexact, so if you are doing fixed point math, just use integers. For display you can simply format using / 100 and % 100.

-Lee

4. ### wrldwzrd89 macrumors G5

Joined:
Jun 6, 2003
Location:
Solon, OH
#4
I was going to suggest something similar - you worded it better than I would have, though. I totally agree with you. Fixed point math is best done in the integer space, because that way you have no precision issues at all.

5. ### gibbz macrumors 68030

Joined:
May 31, 2007
#5
I rewrote it using the ideas the other presented and it worked fine
Code:
```#!/usr/bin/env python
coins = [0,0,0,0,0,0]
recs  = len(coins)

# dollars, half dollars, quarters, dimes, nickels, pennies
values = [100,50,25,10,5,1]

amountString = raw_input('Enter amount: ')
amountList   = amountString.split('.')
amount       = 100*int(amountList[0]) + int(amountList[1])

for x in range(0,recs):
coins[x] = amount//values[x]
amount %= values[x]

print "Dollars: ", coins[0]
print "Half Dollars: ", coins[1]
print "Quarters: ", coins[2]
print "Dimes: ", coins[3]
print "Nickels: ", coins[4]
print "Pennies: ", coins[5]```

Joined:
Jul 17, 2008
Location:
Maryland
7. ### rowsdower macrumors 6502

Joined:
Jun 2, 2009
#7
Alternatively, in python >= 2.4, you can use the decimal module.

Code:
```from decimal import Decimal

coins = [0,0,0,0,0,0]
# dollars, half dollars, quarters, dimes, nickels, pennies
values = [100,50,25,10,5,1]

amountString = raw_input('Enter amount: ')

amount = 100*Decimal(amountString)

for x in range(0,6,1):
coins[x] = amount//values[x]
amount %= values[x]

print "Dollars: ", coins[0]
print "Half Dollars: ", coins[1]
print "Quarters: ", coins[2]
print "Dimes: ", coins[3]
print "Nickels: ", coins[4]
print "Pennies: ", coins[5]
```
I haven't tested this extensively, but it works for your 5.10 example.

8. ### mysterytramp thread starter macrumors 65816

Joined:
Jul 17, 2008
Location:
Maryland
#8
This is a good segue to a larger question I have on Python ... Sometimes these outside libraries are offering different ways to approach a problem that can be solved with the existing language. When is it smarter to take advantage of a library, and when is it smarter to use what the language offers built-in?

This thread generated two versions of the same code. I haven't done any performance testing, but assuming performance is similar, I'd lean toward the first example, which includes:

Code:
```amountList   = amountString.split('.')
amount       = 100*int(amountList[0]) + int(amountList[1])```
Granted, in no way is this "production" code, so the only real question is whether "I" have 2.4, not some other user. Still, I would think "real" programmers would have a preference.

(This reminds me of times when a Scripting Addition solves a problem that can already be solved using plain old AppleScript.)

mt

9. ### rowsdower macrumors 6502

Joined:
Jun 2, 2009
#9
In this case, lee1210's solution is simpler, easy to understand, available in all versions of Python, and probably faster than using the decimal module. I pointed out the decimal module just so that you would know about it in case you need to do precise floating point math in the future.

In general, my opinion is that my time is more valuable than computing time. I hardly ever write programs that are really performance critical, but even when I do I normally write them in a way that makes sense to me first and then go back to do optimizations. Sometimes that means not using a library that I had used for convenience, but was bad for performance. Sometimes that means finding and using a library that I didn't know about, because it does what I need and the code is already optimized.

By the way, if you're interested in performance optimization, Python has some very easy to use profilers. They can tell you what parts of your program take the longest and give you a way to compare different solutions to see which is fastest.

10. ### mysterytramp thread starter macrumors 65816

Joined:
Jul 17, 2008
Location:
Maryland
#10
This is really good advice. I'm still new to Python and I'm hip deep in Unix with the water rising.

mt