JavaScript 浮点数陷阱及解法 https://github.com/camsong/blog/issues/9
//魔鬼的经历
(96.7 + 0.04) === 96.74 //false
96.7 + 0.04 // 96.74000000000001
(96.7 + 0.04).toFixed(2) //"96.74"
(96.7 + 0.04).toFixed(2)*1 //96.74
(96.7 + 0.04).toFixed(2)*1 === 96.74 //true
//原始版本留念 /(ㄒoㄒ)/~~
var currencyUnits = [100, 20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
//抽屉钱数总额
function getDeposit(cid) {
let sum = 0
sum = cid.reduce((cash, obj) => {
return cash + obj[1]
}, 0)
return sum
}
function checkCashRegister(price, cash, cid) {
// console.log(getDeposit(cid))
//需要找的零钱额度
let smallChange = cash - price
console.log("smallChange: " +smallChange)
//抽屉存款够找零
if(getDeposit(cid).toFixed(2)*1 > smallChange) {
console.log(`getDeposit(cid).toFixed(2)*1 > smallChange: ${getDeposit(cid).toFixed(2)*1 > smallChange}`)
console.log(`getDeposit(cid): ${getDeposit(cid)}`)
console.log(`getDeposit(cid).toFixed(2)*1: ${getDeposit(cid).toFixed(2)*1}`)
console.log(`smallChange: ${smallChange}`)
let cidReverseCopy = [...cid].reverse()
let getCashSum = 0
let feedbackCid = {status: "OPEN", change:[]}
//面额从大到小一个一个分析
for(let i=0; i < currencyUnits.length; i++) {
// console.log("面额从大到小一个一个分析 - " + i)
//当前面额 < 找零的钱,那么可以参与找零
if(currencyUnits[i] < (smallChange - getCashSum).toFixed(2)*1) {
//当前面额取几份
for(let j = 1; true; j++) {
// console.log("当前面额取几份 - " + j)
//(当前面额取的钱数 + 前面面额取得的钱数) < 找零的钱
//currencyUnits[i] * j = 面额*张数
//getCashSum = 找零计数
//smallChange = 需要找多少钱
//cidReverseCopy[i][1] = 这个面额有多少钱
if((currencyUnits[i] * j + getCashSum).toFixed(2)*1 <= smallChange &&
(currencyUnits[i] * j).toFixed(2)*1 <= cidReverseCopy[i][1]) {
// console.log("当前面额取几份 - 还可以取一份当前面额" + j)
if(cidReverseCopy[i][0] === "PENNY"){
// console.log(`--------------------------------------------`)
// console.log(`currencyUnits[i] * j: ${currencyUnits[i] * j}`)
// console.log(`getCashSum: ${getCashSum}`)
// console.log(`cidReverseCopy[i][1]: ${cidReverseCopy[i][1]}`)
// console.log(`============================================`)
console.log(`--------------------------------------------`)
console.log(` j: ${j}`)
console.log(`(currencyUnits[i] * j + getCashSum) <= smallChange : `+
`${currencyUnits[i] * j} + ${getCashSum} <= ${smallChange}`)
console.log(`(currencyUnits[i] * j) <= cidReverseCopy[i][1]) : `+
`${currencyUnits[i] * j} <= ${cidReverseCopy[i][1]}`)
// console.log(`cidReverseCopy[i][1]: ${cidReverseCopy[i][1]}`)
console.log(`============================================`)
}
//还可以取一份当前面额
}else {
if(cidReverseCopy[i][0] === "PENNY"){
console.log(`--------------------------------------------`)
console.log(` j: ${j}`)
console.log(`(currencyUnits[i] * j + getCashSum) <= smallChange : `+
`${currencyUnits[i] * j} + ${getCashSum} <= ${smallChange}`)
console.log(`(currencyUnits[i] * j) <= cidReverseCopy[i][1]) : `+
`${currencyUnits[i] * j} <= ${cidReverseCopy[i][1]}`)
// console.log(`cidReverseCopy[i][1]: ${cidReverseCopy[i][1]}`)
console.log(`============================================`)
}
// console.log("当前面额取几份 - 当前面额不用再取了" + j)
//当前面额不用再取了
//先前取得找零的钱 + 当前面额总额 = 目前找零总额
getCashSum += currencyUnits[i] * (j-1)
getCashSum = getCashSum.toFixed(2)*1
//取了几张,记录当前额度的总钱数,要反馈的
if(currencyUnits[i]*(j-1) !== 0)
feedbackCid.change.push([cidReverseCopy[i][0], currencyUnits[i]*(j-1)])
// console.log("当前面额取几份 - 当前面额不用再取了 - feedbackid: " + feedbackCid.change)
// console.log("当前面额取几份 - 额度:" + currencyUnits[i] + " * " + (j-1))
console.log(
`面额额度: ${cidReverseCopy[i][0]}(${currencyUnits[i]}) * ${(j-1)}`+
` = ${getCashSum}`
)
// console.log("当前面额取几份 - 次数:" + (j-1))
break//中止当前循环
}
}
// console.log("取完了一个面额的,够了吗? - getCashSum: " + getCashSum)
//钱取够了,不用再继续分析下一个面额
if(getCashSum === smallChange) {
console.log("抽屉存款够找零 - feedbackCid")
return feedbackCid//进行反馈
}
}
}
if(getCashSum < smallChange) {
console.log("抽屉存款够找零 - feedbackCid")
return {status: "INSUFFICIENT_FUNDS", change: []}
}
}
//抽屉存款不够找零
if(getDeposit(cid).toFixed(2)*1 < smallChange) {
return {status: "INSUFFICIENT_FUNDS", change: []}
}
//抽屉存款 === 找零
if(getDeposit(cid).toFixed(2)*1 === smallChange) {
return {status: "CLOSED", change: cid}
}
// var change = 0;
// return change;
}
// Example cash-in-drawer array:
// [["PENNY", 1.01],
// ["NICKEL", 2.05],
// ["DIME", 3.1],
// ["QUARTER", 4.25],
// ["ONE", 90],
// ["FIVE", 55],
// ["TEN", 20],
// ["TWENTY", 60],
// ["ONE HUNDRED", 100]]
// checkCashRegister(19.5, 20, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]);
// checkCashRegister(3.26, 100, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]])
// checkCashRegister(19.5, 20, [["PENNY", 0.01], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 1], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]])
checkCashRegister(19.5, 20, [["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0], ["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]])
//整理版1
var currencyUnits = [100, 20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
//抽屉钱数总额
function getDeposit(cid) {
let sum = 0
sum = cid.reduce((cash, obj) => {
return cash + obj[1]
}, 0)
return sum
}
function checkCashRegister(price, cash, cid) {
//需要找的零钱额度
let smallChange = cash - price
//抽屉存款够找零
if(getDeposit(cid).toFixed(2)*1 > smallChange) {
let cidReverseCopy = [...cid].reverse()
let getCashSum = 0
let feedbackCid = {status: "OPEN", change:[]}
//面额从大到小一个一个分析
for(let i=0; i < currencyUnits.length; i++) {
//当前面额 < 找零的钱,那么可以参与找零
if(currencyUnits[i] < (smallChange - getCashSum).toFixed(2)*1) {
//当前面额取几份
for(let j = 1; true; j++) {
//(当前面额取的钱数 + 前面面额取得的钱数) < 找零的钱
//currencyUnits[i] * j = 面额*张数
//getCashSum = 找零计数
//smallChange = 需要找多少钱
//cidReverseCopy[i][1] = 这个面额有多少钱
if((currencyUnits[i] * j + getCashSum).toFixed(2)*1 <= smallChange &&
(currencyUnits[i] * j).toFixed(2)*1 <= cidReverseCopy[i][1]) {
//还可以取一份当前面额
}else {
//当前面额不用再取了
//先前取得找零的钱 + 当前面额总额 = 目前找零总额
getCashSum += currencyUnits[i] * (j-1)
getCashSum = getCashSum.toFixed(2)*1
//取了几张,记录当前额度的总钱数,要反馈的
if(currencyUnits[i]*(j-1) !== 0)
feedbackCid.change.push([cidReverseCopy[i][0], currencyUnits[i]*(j-1)])
break//中止当前循环
}
}
if(getCashSum === smallChange) {
return feedbackCid//进行反馈
}
}
}
if(getCashSum < smallChange) {
return {status: "INSUFFICIENT_FUNDS", change: []}
}
}
//抽屉存款不够找零
if(getDeposit(cid).toFixed(2)*1 < smallChange) {
return {status: "INSUFFICIENT_FUNDS", change: []}
}
//抽屉存款 === 找零
if(getDeposit(cid).toFixed(2)*1 === smallChange) {
return {status: "CLOSED", change: cid}
}
}
checkCashRegister(19.5, 20,
[["PENNY", 0.5], ["NICKEL", 0], ["DIME", 0], ["QUARTER", 0], ["ONE", 0],
["FIVE", 0], ["TEN", 0], ["TWENTY", 0], ["ONE HUNDRED", 0]])
测试数据:
···
//别人家的孩子
//TL;NR
//2019-11-03, 02:51
//(●'◡'●)
// Create an array of objects which hold the denominations and their values
var denom = [
{ name: "ONE HUNDRED", val: 100.0 },
{ name: "TWENTY", val: 20.0 },
{ name: "TEN", val: 10.0 },
{ name: "FIVE", val: 5.0 },
{ name: "ONE", val: 1.0 },
{ name: "QUARTER", val: 0.25 },
{ name: "DIME", val: 0.1 },
{ name: "NICKEL", val: 0.05 },
{ name: "PENNY", val: 0.01 }
];
function checkCashRegister(price, cash, cid) {
var output = { status: null, change: [] };
var change = cash - price;
// Transform CID array into drawer object
var register = cid.reduce(
function(acc, curr) {
acc.total += curr[1];
acc[curr[0]] = curr[1];
return acc;
},
{ total: 0 }
);
// Handle exact change
if (register.total === change) {
output.status = "CLOSED";
output.change = cid;
return output;
}
// Handle obvious insufficient funds
if (register.total < change) {
output.status = "INSUFFICIENT_FUNDS";
return output;
}
// Loop through the denomination array
var change_arr = denom.reduce(function(acc, curr) {
var value = 0;
// While there is still money of this type in the drawer
// And while the denomination is larger than the change remaining
while (register[curr.name] > 0 && change >= curr.val) {
change -= curr.val;
register[curr.name] -= curr.val;
value += curr.val;
// Round change to the nearest hundreth deals with precision errors
change = Math.round(change * 100) / 100;
}
// Add this denomination to the output only if any was used.
if (value > 0) {
acc.push([curr.name, value]);
}
return acc; // Return the current change_arr
}, []); // Initial value of empty array for reduce
// If there are no elements in change_arr or we have leftover change, return
// the string "Insufficient Funds"
if (change_arr.length < 1 || change > 0) {
output.status = "INSUFFICIENT_FUNDS";
return output;
}
// Here is your change, ma'am.
output.status = "OPEN";
output.change = change_arr;
return output;
}
// test here
checkCashRegister(19.5, 20.0, [
["PENNY", 1.01],
["NICKEL", 2.05],
["DIME", 3.1],
["QUARTER", 4.25],
["ONE", 90.0],
["FIVE", 55.0],
["TEN", 20.0],
["TWENTY", 60.0],
["ONE HUNDRED", 100.0]
]);
让人头秃的草纸