anon2000
anon2000

Reputation: 57

Listing what coins are needed for given amount python

I need to write a function that prints the amount of UK coins needed for a given amount, in a list format (i.e. 8 values in the list for £2 coins, £1 coins, £0.50, £0.20, £0.10, £0.05, £0.02, £0.01).

I have so far written the following:

def pay_with_coins( amount ):

coins_list = [0, 0, 0, 0, 0, 0, 0, 0]

if amount == 0:
    return(coins_list)
else:
    while amount > 2.00:
        coins_list[0] = (coins_list[0] + 1)
        amount = amount - 2.00
    while amount >= 1.00 and amount < 2.00:
        coins_list[1] = (coins_list[1] + 1)
        amount = amount - 1.00
    while amount >= 0.50 and amount < 1.00:
        coins_list[2] = (coins_list[2] + 1)
        amount = amount - 0.50
    while amount >= 0.20 and amount < 0.50:
        coins_list[3] = (coins_list[3] + 1)
        amount = amount - 0.20
    while amount >= 0.10 and amount < 0.20:
        coins_list[4] = (coins_list[4] + 1)
        amount = amount - 0.10
    while amount >= 0.05 and amount < 0.10:
        coins_list[5] = (coins_list[5] + 1)
        amount = amount - 0.05
    while amount >= 0.02 and amount < 0.05:
        coins_list[6] = (coins_list[6] + 1)
        amount = amount - 0.02
    while amount >= 0.01 and amount < 0.05:
        coins_list[7] = (coins_list[7] + 1)
        amount = amount - 0.01
    return(coins_list)

I am testing the function by passing the following:

print(pay_with_coins(0.08))
print(pay_with_coins(8.02))
print(pay_with_coins(1.74))
print(pay_with_coins(1001))

This is what I'm supposed to get:

[0,0,0,0,0,1,1,1]

[4,0,0,0,0,0,1,0]

[0,1,1,1,0,0,2,0]

[500,1,0,0,0,0,0,0]

And this is what I actually get:

[0, 0, 0, 0, 0, 1, 1, 0]

[4, 0, 0, 0, 0, 0, 0, 1]

[0, 1, 1, 1, 0, 0, 1, 1]

[500, 1, 0, 0, 0, 0, 0, 0]

As you can see, the last two values in the list is where it seems to mess up and I'm not quite sure what the issue is.

I have a feeling that the last two values are messing up because they're 0.05 and 0.01 (2 decimal places). Any idea how to sort that out?

Upvotes: 0

Views: 786

Answers (4)

Jaakko2000
Jaakko2000

Reputation: 36

You can use Python's decimal-module for that. It represents numbers as decimals (base 10) instead of the normal base 2 used in computers and thus it can represent numbers like 1.1.

Code would go something like this:

from decimal import Decimal

def pay_with_coins( amount ):
    amount = Decimal(amount)
    coins_list = [0, 0, 0, 0, 0, 0, 0, 0]

    if amount == 0:
        return(coins_list)
    else:
        while amount > Decimal("2.00"):
            coins_list[0] = (coins_list[0] + 1)
            amount = amount - Decimal("2.00")
        while amount >= Decimal("1.00") and amount < Decimal("2.00"):
            coins_list[1] = (coins_list[1] + 1)
            amount = amount - Decimal("1.00")
        while amount >= Decimal("0.50") and amount < Decimal("1.00"):
            coins_list[2] = (coins_list[2] + 1)
            amount = amount - Decimal("0.50")
        while amount >= Decimal("0.20") and amount < Decimal("0.50"):
            coins_list[3] = (coins_list[3] + 1)
            amount = amount - Decimal("0.20")
        while amount >= Decimal("0.10") and amount < Decimal("0.20"):
            coins_list[4] = (coins_list[4] + 1)
            amount = amount - Decimal("0.10")
        while amount >= Decimal("0.05") and amount < Decimal("0.10"):
            coins_list[5] = (coins_list[5] + 1)
            amount = amount - Decimal("0.05")
        while amount >= Decimal("0.02") and amount < Decimal("0.05"):
            coins_list[6] = (coins_list[6] + 1)
            amount = amount - Decimal("0.02")
        while amount >= Decimal("0.01") and amount < Decimal("0.05"):
            coins_list[7] = (coins_list[7] + 1)
            amount = amount - Decimal("0.01")
        return(coins_list)
print(pay_with_coins("1.74"))

Notice that the call is now made with a string, but you can also pass it a Decimal object and it won't get angry at you.

Upvotes: 1

Hampus Larsson
Hampus Larsson

Reputation: 3100

What you're looking for, is a way to divide a number into smaller and smaller numbers. one way do do that is via divmod

def pay_with_coins(amount):
    twoer,rest=divmod(amount, 2) # £2
    onner,rest=divmod(rest, 1) # £1
    halfer,rest=divmod(rest, 0.5) # £0.50
    fifther,rest=divmod(rest, 0.20) # £0.20
    tenther,rest=divmod(rest, 0.10) # £0.10
    twenthier,rest=divmod(rest, 0.05) # £0.05
    fifthier,rest=divmod(rest, 0.02) # £0.02
    hundreder,rest=divmod(rest,0.01) # £0.01
    coinList = [twoer, onner, halfer, fifther, tenther, twenthier,fifthier, hundreder]
    return [i for i in map(int, coinList)]

In the above code, i use divmod continually on the same variable, to separate the values into lower and lower denominations.


It seems like I did not read the question quite fully before creating my "solution" for this. Reading the other answer to this question, where they recommend to do the calculation with cents, so as to not use unnecessary floating-points numbers, I've also edited my code to follow this:

def pay_with_coins(amount):
    amount *= 100
    twoer,rest=divmod(amount,200) # £2
    onner,rest=divmod(rest,100) # £1
    halfer,rest=divmod(rest,50) # £0.50
    fifther,rest=divmod(rest,20) # £0.20
    tenther,rest=divmod(rest,10) # £0.10
    twenthier,rest=divmod(rest,5) # £0.05
    fifthier,rest=divmod(rest,2) # £0.02
    hundreder,rest=divmod(rest,1) # £0.01
    coinList = [twoer, onner, halfer, fifther, tenther, twenthier,fifthier, hundreder]
    return [i for i in map(int, coinList)]

The only real difference is that I multiply the given amount by 100, and did the same with the calculations so avoid floating-point numbers all together.

Upvotes: 0

Cohan
Cohan

Reputation: 4544

As @Paritosh Singh stated in his answer, there is an issue with floats. But if you'd like a solution that is a bit more expandable, you could try the following approach which will save a lot of typing.

# Create list of currencies
currencies = [2.00, 1.00, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01]

def pay_with_coins(amount):

    # Initialize array
    coins = [0 for i in range(len(currencies))]

    # Adjust to integers to avoid floating point issues
    amount = int(amount * 100)
    values = [c * 100 for c in currencies]

    # Loop throug values
    for currency in values:
        i = values.index(currency)
        coins[i] = 0

        # Dish out coins until you need to move to a smaller value
        while amount >= currency:
            amount -= currency
            coins[i] += 1

    return coins


print(pay_with_coins(0.08)) #[0, 0, 0, 0, 0, 1, 1, 1]
print(pay_with_coins(8.02)) #[4, 0, 0, 0, 0, 0, 1, 0]
print(pay_with_coins(1.74)) #[0, 1, 1, 1, 0, 0, 2, 0]
print(pay_with_coins(1001)) #[500, 1, 0, 0, 0, 0, 0, 0]

Upvotes: 1

Paritosh Singh
Paritosh Singh

Reputation: 6246

Ah, i fear this is one of the worst ways to find out about the limitations of binary systems around floating point arithmetic.

It is not possible to accurately represent every decimal number in binary notation. https://docs.python.org/3.7/tutorial/floatingpoint.html

To avoid the issue, when it comes to currency, use cents as your base unit and avoid floats completely.

def pay_with_coins( amount_in_cents ):
coins_list = [0, 0, 0, 0, 0, 0, 0, 0]    
if amount_in_cents == 0:
    return(coins_list)
else:
    while amount_in_cents > 200:
        coins_list[0] = (coins_list[0] + 1)
        amount_in_cents = amount_in_cents - 200
    while amount_in_cents >= 100 and amount_in_cents < 200:
        coins_list[1] = (coins_list[1] + 1)
        amount_in_cents = amount_in_cents - 100
    while amount_in_cents >= 50 and amount_in_cents < 100:
        coins_list[2] = (coins_list[2] + 1)
        amount_in_cents = amount_in_cents - 50
    while amount_in_cents >= 20 and amount_in_cents < 50:
        coins_list[3] = (coins_list[3] + 1)
        amount_in_cents = amount_in_cents - 20
    while amount_in_cents >= 10 and amount_in_cents < 20:
        coins_list[4] = (coins_list[4] + 1)
        amount_in_cents = amount_in_cents - 10
    while amount_in_cents >= 5 and amount_in_cents < 10:
        coins_list[5] = (coins_list[5] + 1)
        amount_in_cents = amount_in_cents - 5
    while amount_in_cents >= 2 and amount_in_cents < 5:
        coins_list[6] = (coins_list[6] + 1)
        amount_in_cents = amount_in_cents - 2
    while amount_in_cents >= 1 and amount_in_cents < 2:
        coins_list[7] = (coins_list[7] + 1)
        amount_in_cents = amount_in_cents - 1
    return(coins_list)

Upvotes: 2

Related Questions