Auć. W skrócie, to nie Twoja wina. To jedna ze typowych pułapek czyhających na początkujących – binarne floaty.
W dużym skrócie: z podanych nominałów (5, 2, 1, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01) tylko pierwsze kilka z nich istnieją jako dokładne reprezentacje we floatach (5, 2, 1, 0.50), pozostałe istnieją tylko jako przybliżenia. Na przykład 0.10 to:
>>> "%.50f" % 0.10
'0.10000000000000000555111512312578270211815834045410'
I te drobne różnice w przybliżeniach wszystko psują.
Przykładowo, jeśli weźmiesz resztę 0.03, to wychodzi coś takiego:
>>> reszta = 0.03
>>> "%.50f" % reszta
'0.02999999999999999888977697537484345957636833190918'
>>> reszta -= 0.02
>>> "%.50f" % reszta
'0.00999999999999999847344334114040975691750645637512'
0.0099999999..., które zostaje, to oczywiście mniej niż 0.01, więc program nie wypisze ostatniego grosza – to problem który opisałeś, nie?
Rozwiązań jest kilka:
1) Najlepiej nigdy nie robić operacji finansowych na typie float(). Od tego w Pythonie i wielu innych językach jest typ Decimal. Przykładowo:
from decimal import *
getcontext().prec = 6
NOMINALY = (
Decimal(5), Decimal(2), Decimal(1), Decimal(0.50),
Decimal("0.20"), Decimal("0.10"), Decimal("0.05"),
Decimal("0.02"), Decimal("0.01")
)
N = len(NOMINALY)
def WydajReszte(reszta):
i = 0
while reszta > 0 and i < N:
if reszta >= NOMINALY[i]:
print(NOMINALY[i])
reszta = reszta - NOMINALY[i]
else:
i += 1
reszta = Decimal(input("Podaj kwotę wydania reszty: "))
WydajReszte(reszta)
2) Alternatywnie można porównania robić z tzw "epsilonem", czyli jakąś dokładnością (np. do 0.00001 w tym przypadku). To niestety komplikuje ify, bo zamiast normalnie robić "if reszta >= NOMINALY[i]" trzeba policzyć różnicę pomiędzy nimi i (w przypadku nierówności w którąś stronę) dodać lub odjąć od różnicy epsilon (ew epsilon/2), i sprawdzić czy to mniej czy więcej niż 0.
a = 0.01
b = 0.03
b -= 0.02
print(a, b, a==b)
E = 0.00001
diff = b - a
diff += E / 2 # dla b >= a
#diff -= E / 2 # dla b <= a
if diff >= 0:
print("wieksze lub rowne z dokladnoscia do E")
else:
print("mniejsze")
# Dla a == b by było:
if abs(a - b) < E:
print("rowne z dokladnoscia do E")
else:
print("nierowne")
"""
OUTPUT:
0.01 0.009999999999999998 False
wieksze lub rowne z dokladnoscia do E
rowne z dokladnoscia do E
"""
Generalnie da się, ale szczerze sugeruje użyć Decimali po prostu w tym przypadku.