为什么JavaScript收银机找零函数的change变量会出现异常数值
问题排查结果
1. 核心原因:JavaScript浮点数精度丢失
JavaScript采用双精度浮点数存储数值,小数无法被精确二进制编码,多次加减运算后会出现精度偏移,比如0.1 + 0.2结果为0.30000000000000004,你看到的奇怪change值就是这个特性导致的。你的测试用例初始找零是0.6,多次减面值后就会出现类似0.09999999999999987的异常值。
2. 逻辑判断错误
你的条件判断存在三处逻辑错误,会导致找零流程无法按预期执行:
- 条件
change < cid[i][1]逻辑错误:你需要判断的是「当前币种的剩余总额足够支取至少一张该面值货币」,而非「剩余找零小于当前币种总额」,比如找零10元时,10大于QUARTER的总金额4.25,但你依然可以用QUARTER找零,这个条件会错误跳过可用面值。 - 条件
change > money[cid[i][0]]缺少等于判断:当剩余找零刚好等于当前面值时,该条件不成立,无法完成最后一次扣减,会进入死循环。 - 缺少边界校验:当
i递减到-1时,依然会访问cid[i]造成数组越界,同时如果无可用面值找零时会进入死循环。
修复方案
- 所有金额统一转为整数(单位为分)计算,彻底避免浮点数精度问题
- 修正条件判断逻辑,补充边界校验
修复后代码示例:
function checkCashRegister(price, cash, cid) { // 所有金额转成分,转为整数计算 let change = Math.round((cash - price) * 100) const money = { 'PENNY': 1, 'NICKEL':5, 'DIME':10, 'QUARTER':25, 'ONE':100, 'FIVE':500, 'TEN':1000, 'TWENTY':2000, 'ONE HUNDRED':10000 } // cid也转成整数 const cidCent = cid.map(item => [item[0], Math.round(item[1] * 100)]) console.log(change/100, 'start') let i = 8 while(change > 0) { // 边界校验,没有可用面值时跳出 if (i < 0) { console.log('无足够零钱找零') break } const faceVal = money[cidCent[i][0]] const cidTotal = cidCent[i][1] // 修正判断条件:面值小于等于找零,且抽屉有该面值的钱 if (faceVal <= change && cidTotal >= faceVal) { console.log(cidCent[i][0], 'cid') console.log(faceVal/100, 'money') change -= faceVal cidCent[i][1] -= faceVal // 扣减抽屉里的钱 console.log(change/100, 'change') i = 8 // 重置为最大面值重新遍历 } i-- } } checkCashRegister(19.4, 20, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]);
内容的提问来源于stack exchange,提问作者Chris H.
相关产品推荐
相关产品推荐

