PDA

View Full Version : python3 decimal help



Balain
2012-09-26, 12:45 AM
Okay i'm fairly new to python, I have sent in my first assignment, using the techniques we have covered in class so far.

I used the trick:

price = float(input("\nEnter before tax price $")
price = int((price*1.05)*100)/100

However with large numbers the calculations fail because of the way computers handle floats, and we can't use round (dealing with money)

I tried splitting the dollars to an int and the cents to an int, using the same trick as above. However again with large numbers, calculations are off by a penny.

So I've been fooling around with decimals, but can't get it to work for me.

I tried:

import decimal

decimal.prec=2

decimal.Decimal(3.99*1.05)

I thought I should get Decimal('4.18')

but instead I get Decimal('4.189xxxxxxxxxxxxxxxxxxxxxxx')

so what am I doing wrong with decimal?

Generic Archer
2012-09-26, 01:05 AM
um, check your result... 3.99*1.05 =4.1895


Beyond that I don't know python, but why are you multiplying then dividing by 100? To my mind you could just drop that bit completely.

And given that you are multiplying you will have to either parse your inputs, or round to 2 decimal places... the rules don't change just because it's money.

Balain
2012-09-26, 01:56 AM
From reading up on the module decimal you can set the precision and it only shows the number of decimal places. So using 3.99*1.05 in decimal, with precision set to 2 I should see 4.18, which is what I want. I could round 3.99*1.05 but then you get 4.19, which is very bad when dealing with money, because the cost should be 4.18 not 4.19.

When computers were first introduced to banking, accountants became filthy rich by having the computer take(steal) all the half pennies from there thousands of accounts put them into there own accounts after rounding up. The clients where fine they didn't see a difference in the balance sheet. half a penny round up doesn't sound like much but when you have say 100 clients doing 10 to 100 transactions a day, those accountants could get $10 to $100 a day off those pennies. That's why banks now use ints for the dollar amounts and ints for the cents.

Anyways The the multiplying and dividing by 100 is a trick to get 4.18 (using 3.99*1.05)

int((3.99*1.05)*100) = 418.

418/100 = 4.18 left with .18 with no rounding.

Like I said this fails with large numbers, say 100000000 or more. It's not likely someone would put in a price of $100000000 and pay with say $100000000. I tried using a series of loops adding the the dollars, quarters, dimes etc that would be change and subtracting the amount paid. Which also works not too bad, but again with large numbers like 10000000 it works but takes forever to run.

That's why using the decimal module seems to be a far better idea but I seem to be doing something wrong.

Jimorian
2012-09-26, 05:32 AM
Not sure how it is in Canada, but in the U.S., sales taxes ARE rounded up if the floating result is a half penny or more.

Teddy
2012-09-26, 04:07 PM
Okay i'm fairly new to python, I have sent in my first assignment, using the techniques we have covered in class so far.

I used the trick:

price = float(input("\nEnter before tax price $")
price = int((price*1.05)*100)/100

[...]

May I suggest that instead of doing 1.05 * 100, just use the integer value 105 instead. That should hopefully clear up at least some imprecision.

You can also prompt the user to input the price in whole cents instead. That'll let you get around the first float as well. :smallwink:

When you get a bit more advanced, you'll learn more useful functions (methods? I'm not quite aware of what Python calls them), and eventually even to parse numbers out of a string yourself. I remember the frustration from not being able to solve really simple problems when I begun my first programming class three years ago, but don't worry, you'll learn in due time.

TSGames
2012-09-26, 09:07 PM
I don't have the time to run a test right now, but I did find a page in the Python 3 documentation that may be very relevant to your predicament (http://docs.python.org/py3k/library/decimal.html?highlight=decimal#mitigating-round-off-error-with-increased-precision).

[EDIT]
Also, have you tried solving the problem by not solving it? I.e. you have "418" already when you need "4.18', maybe you could use print string formatting (http://www.wellho.net/mouth/496_Python-printf.html) to simply display it as a decimal instead? That way you don't have to deal with rounding errors at all.

nedz
2012-09-27, 03:53 PM
I don't know python, but I do know the standard approach.

Input the text as a string
Look for the '.'
Parse each substring as ints
(Python may have a standard method for doing this - check the reference manual, or there may be some trick)
Its messy, but there you go.

Figuring out how to handle the case where there is no '.' I'll leave as an exercise.

You're float trick will always fail for large numbers because you will exceed the precision. 32 bit floats only have 24 bits of precision, and one of those is a sign. 2**23 = 8388608 = 8.388608 x 10**6 which with two decimal places limits you to 83,886. Actually its one bit worse than this, because the least bit is random so 41,943

You can possibly use doubles, but that just moves the problem up to larger numbers. This might be adequate for your code, but its not correct.

Neftren
2012-09-27, 07:09 PM
I don't know python, but I do know the standard approach.

Input the text as a string
Look for the '.'
Parse each substring as ints
(Python may have a standard method for doing this - check the reference manual, or there may be some trick)
Its messy, but there you go.

Figuring out how to handle the case where there is no '.' I'll leave as an exercise.

You're float trick will always fail for large numbers because you will exceed the precision. 32 bit floats only have 24 bits of precision, and one of those is a sign. 2**23 = 8388608 = 8.388608 x 10**6 which with two decimal places limits you to 83,886. Actually its one bit worse than this, because the least bit is random so 41,943

You can possibly use doubles, but that just moves the problem up to larger numbers. This might be adequate for your code, but its not correct.


sales_tax = 1.05 # Enter Your Sales Tax
price = input ('Please Enter the Sticker Price: $')
price = str(price).partition('.') # Split a Decimal

# Handle the Calculation on Integer Values
left = int(price[0]) * sales_tax
right = int(price[2]) * sales_tax
price = left + right / 100.0
print price

I believe that's the way you described it.



Balain, what exactly are the parameters? What size data type are you using? Since this is a Finance oriented program, it would be logical to use the decimal package. Here is an example of how it might be used to solve your problem:


from decimal import *

sales_tax = 1.05 # Enter Your Sales Tax
price = input ('Please Enter the Sticker Price: $')

price = Decimal(price)
sales_tax = Decimal(sales_tax)
price *= sales_tax

print price
print '%.2f' % price

I used some string formatting there at the end. Note that this is all Python 2.7 compliant. I do not maintain a Python 3 install at this time, but you might also try the format() function.