Python金额面值计算循环处理小数时0.01面值计数错误问题
问题:Python处理小数金额找零计算时的精度错误
我正在学习Python,需要根据输入金额1575.78计算1000、500、200、100、50、20、10、5、1、0.25、0.01这些面值的数量。但编写的代码处理该小数金额时出现错误,整数金额(比如1500、20)计算结果正确。
错误代码
def withdraw_money(): denoms = (1000, 500, 200, 100, 50, 20, 10,5,1,.25,0.01) while True: try: withdraw = 1575.770 break except Exception as e: print('Incorrect input: %s' % e) print("Here is the bill breakdown for the amount input") for d in denoms: count = withdraw // d print('P%i = %i' % (d, count)) withdraw -= count * d withdraw_money()
当前错误输出
Here is the bill breakdown for the amount input P1000 = 1 P500 = 1 P200 = 0 P100 = 0 P50 = 1 P20 = 1 P10 = 0 P5 = 1 P1 = 0 P0.25 = 3 P0.01 = 2
其中P0.01 = 2应为P0.01 =3
问题原因
这是浮点数的精度缺陷导致的。计算机以二进制存储浮点数,0.01这类十进制小数无法被二进制精确表示,计算过程中会产生微小误差。比如1575.78经过前面的面值扣除后,剩余金额可能是一个略小于0.03的数(如0.029999999999994316),用这个数除以0.01时,//取整得到的就是2而非3。
解决方法
处理金额计算时,优先用整数运算或高精度十进制运算避免浮点数误差,以下两种方案都可行:
方案1:转换为整数(以分为单位)
把所有金额转换为分(整数),彻底规避浮点数问题:
def withdraw_money(): # 面值转换为分(整数) denoms = (100000, 50000, 20000, 10000, 5000, 2000, 1000, 500, 100, 25, 1) while True: try: # 输入金额转成分,取整避免精度误差 withdraw = int(round(1575.78 * 100)) break except Exception as e: print('Incorrect input: %s' % e) print("Here is the bill breakdown for the amount input") for d in denoms: count = withdraw // d # 输出时转回原面值 print('P%.2f = %i' % (d / 100, count)) withdraw -= count * d withdraw_money()
正确输出:
Here is the bill breakdown for the amount input P1000.00 = 1 P500.00 = 1 P200.00 = 0 P100.00 = 0 P50.00 = 1 P20.00 = 1 P10.00 = 0 P5.00 = 1 P1.00 = 0 P0.25 = 3 P0.01 = 3
方案2:使用decimal模块处理高精度十进制
用Python内置的decimal模块直接处理十进制数,保留精确的小数运算:
from decimal import Decimal, getcontext def withdraw_money(): # 设置足够的计算精度 getcontext().prec = 20 denoms = (Decimal('1000'), Decimal('500'), Decimal('200'), Decimal('100'), Decimal('50'), Decimal('20'), Decimal('10'), Decimal('5'), Decimal('1'), Decimal('0.25'), Decimal('0.01')) while True: try: withdraw = Decimal('1575.78') break except Exception as e: print('Incorrect input: %s' % e) print("Here is the bill breakdown for the amount input") for d in denoms: count = withdraw // d print('P%s = %i' % (d, count)) withdraw -= count * d withdraw_money()
该方案无需手动转换单位,适合对精度要求高的场景,同样能得到正确的找零结果。
内容的提问来源于stack exchange,提问作者MiksMeister
相关产品推荐
相关产品推荐

