Applying Decimal in Python

Question:

I am trying to solve the following question:

Suppose the cover price of a book is $24.95, but bookstores get a 40% discount. Shipping costs $3 for the first copy and 75 cents for each additional copy. What is the total wholesale cost for 60 copies?

Old attempt with reference to Nic3500:

book = 24.95
discount_percentage = 0.4
shipping = 3.0
reduced_shipping = 0.75
quantity = 60

discount = book * discount_percentage
print(discount)
wholesale_price = book - discount
print(wholesale_price)
total_books = wholesale_price * quantity
print(total_books)
total_shipping = shipping + (reduced_shipping * quantity)
print(total_shipping)

cost = total_books + total_shipping

print('$', cost)

New attempt using decimal with reference to Michael Butscher:

from decimal import *

getcontext().prec = 2

book = Decimal(24.95)
discount_percentage = Decimal(0.4)
shipping = Decimal(3.0)
reduced_shipping = Decimal(0.75)
quantity = Decimal(60)

discount = book * discount_percentage
print(discount)
wholesale_price = book - Decimal(discount)
print(wholesale_price)
total_books = Decimal(wholesale_price) * quantity
print(total_books)
total_shipping = shipping + (reduced_shipping * (quantity - 1))
print(total_shipping)

cost = Decimal(total_books) + Decimal(total_shipping)

print('$', cost)

The problem however is the answer should be $945.45. Due to the issues with floating point numbers somewhere in the calculation I receive the wrong answer and use that. I have looked up using the decimal module but don’t understand how I would apply it to my problem, any help is appreciated, thanks.

Asked By: josh139

||

Answers:

Your calculation of the total_shipping is wrong. It says 1st copy at 3$, others at 0.75$. So you should have 3$ + (59 * 0.75$).

The other thing is you should round() your amounts, since we do not go below 2 digits precision for $.

Hence:

#!/usr/bin/python3

book = 24.95
discount_percentage = 0.4
shipping = 3.0
reduced_shipping = 0.75
quantity = 60

discount = round(book * discount_percentage,2)
print(discount)

wholesale_price = round(book - discount,2)
print(wholesale_price)

total_books = round(wholesale_price * quantity,2)
print(total_books)

total_shipping = round(shipping + (reduced_shipping * (quantity - 1)),2)
print(total_shipping)

cost = round(total_books + total_shipping,2)

print('$', cost)

Output:

9.98
14.97
898.2
47.25
$ 945.45
Answered By: Nic3500

The reason you should use decimals when dealing with money is that floating point numbers don’t represent all decimal numbers correctly. See What Every Computer Scientist Should Know About Floating-Point Arithmetic

You only need to define your floating point numbers as Decimals. There is a difference between what you’ve saved in your variables, and what you display to the user. It is perfectly fine to store your data to much higher precision, and only display two digits.

When you do this, and fix the error in your equation that Nic pointed out, you get this

from decimal import Decimal

book = Decimal('24.95')
discount_percentage = Decimal('0.4')
shipping = Decimal('3.0')
reduced_shipping = Decimal('0.75')
quantity = 60

discount = book * discount_percentage
wholesale_price = book - discount
total_books = wholesale_price * quantity
total_shipping = shipping + (reduced_shipping * (quantity - 1))

cost = total_books + total_shipping

print(f'${cost:.2f}')

Which outputs $945.45

Notice I used an f-string to format the output to 2 decimal places. You can read more about the f-string syntax at https://realpython.com/python-f-strings/


The issue with setting decimal.getcontext().prec is that it limits the number of significant figures available for all decimal operations. 9.5e+2 is two significant digits

from decimal import Decimal, getcontext

getcontext().prec = 2
d1 = Decimal('1234.56')
d2 = d1 + 1
print(d2) # 1.2E+3

If you want to round the decimal to a fixed number of decimal places, use the quantize method:

TWO_DECIMALS = Decimal('0.01')
print(cost.quantize(TWO_DECIMALS)) # Prints 945.45
Answered By: Pranav Hosangadi