Cash Register and the revelation that computers can't do dimes (at least without any workaround)

Tell us what’s happening:
After reading about floats and how they are portrayed in binary, and learning about the Number object, and painstakingly debugging the rounding problem, I finally slapped together this monstrosity of a code.

There is a few things that I’m not happy with in it though, mostly the performance aspect, so I hope someone can give me advice on them:

  1. I have to use 2 for loops in the giveNum function just to make a new array where the value of each bill is added so it can be worked on easier, and that makes 81 boolean checks just for 9 actions. I wonder if I can make it more efficient, maybe array-wide methods or binary checking can do so, but I can’t figure out how to make that work.

  2. The subfunction calcAmountOfBill originally was:

let calcAmountOfBill = (num,i) => {
  let max = cid[i][1]/cid[i][2];
  let div = ((num-(num%cid[i][2]))/cid[i][2]);
  return div>=max?max:div;
} 

instead of painstakingly running a loop until the double brake hits (50 pennies). But the loops holds against the floats, and my cheeky math doesn’t.

I can’t find a way to avoid the rounding problem to make it works correctly with floats. The inaccuracy either stacks each time the function loops for each bill until it’s apparent, or show up at the last computation (for the 50 pennies check, it always either returns remain as a non-0 value, or return div=49 instead of the correct 50) no matter what type of rounding technique I use. If anyone have any insight or a solution to help it works, please teach me.

  1. I’m not really happy about how this piece of code has to repeat itself instead of being put inside the subfunction calcAmountOfBill (was my initial plan):
if (j!=0) {
        changeArr.push([arr[i][0],arr[i][2]*j]);
        remain = floatFixer(remain-arr[i][2]*j);
        arr[i][1] -= arr[i][2]*j;  
      } 

When I tested it, I figured out changeArr and remain was only updated locally and not carried over into the main function, so I have to bring them out. I wonder if there is a way for you to return things like the result of an Array.push, or an upper-layer array element’s edit from inside a function into the upper-layer.

That’s everything, thank you for reading. I’m sure some people will find these questions ridiculous, but if you can give me answers and your insight on them, you are a great and smart person and I love you for that :grin:

Your code so far

let unit = [
    ["PENNY", 0.01],
    ["NICKEL", 0.05],
    ["DIME", 0.1],
    ["QUARTER", 0.25],
    ["ONE", 1],
    ["FIVE", 5],
    ["TEN", 10],
    ["TWENTY", 20],
    ["ONE HUNDRED", 100]  
  ]
  
function checkCashRegister(price, cash, cid) {
  let change = cash-price;
  let total = null;
  for (let i of cid) {
    total += i[1];
  }
  total = floatFixer(total);
  let calc = changeCalc(change,giveNum(cid));
  let changeStack = calc[0];
  let unpaid = calc[1];
  let result = new Object;
  if (unpaid != 0) {
    result.status = "INSUFFICIENT_FUNDS";
    result.change = [];
  }  else if (total == change&&unpaid == 0) {
    result.status = "CLOSED";
    result.change = cid;
  } else {
    result.status = "OPEN";
    result.change = changeStack;
  }
  return result;
}


function floatFixer (num) {
  num = Number((num*(1+Number.EPSILON)).toFixed(2));
  return num;
}

function giveNum (arr) {
  let dup = JSON.parse(JSON.stringify(arr));
  for (let i = dup.length-1;i >= 0;i--) {
    for (let j of unit) {
      if (dup[i][0]==j[0]) {
        dup[i].push(j[1]);
      }
    }
  }
  return dup;
}

function changeCalc(change,arr) {
  let remain = change;
  let changeArr = new Array;
  let calcAmountOfBill = (num,i) => {
    let j = 0;
    while (arr[i][2]*(j+1)<=num&&arr[i][1]>arr[i][2]*j) {j++};
    return j;
  }
  if (remain>=arr[arr.length-1][2]) {
    let j = calcAmountOfBill(remain,arr.length-1);
    if (j!=0) {
      changeArr.push([arr[arr.length-1][0],arr[arr.length-1][2]*j]);
      remain = floatFixer(remain-arr[arr.length-1][2]*j);
      arr[arr.length-1][1] -= arr[arr.length-1][2]*j; 
    }
  };
  for (let i = arr.length-2;i >= 0;i--) {
    if (arr[i][2]<=remain) {
      let j = calcAmountOfBill(remain,i);
      if (j!=0) {
        changeArr.push([arr[i][0],arr[i][2]*j]);
        remain = floatFixer(remain-arr[i][2]*j);
        arr[i][1] -= arr[i][2]*j;  
      }    
    }
  }  
  return [changeArr,remain];
} 

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]]);

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/110.0

Challenge: JavaScript Algorithms and Data Structures Projects - Cash Register

Link to the challenge:

1 Like
  1. You might opt to assume that both arrays have corresponding denominations at the same indices. Some other option could be using object for units, instead of array, it’d map denomination name to the value without needs for additional checks or nested for loop.
  2. Instead of finding another way to try to correct float imprecision, have you thought what can be done to eliminate, or limit the need to make calculations on floats?
  3. If the input to function is mutable object (ie. like array or object), then function could just change it directly. Keep in mind that’s not always the best idea, without care it might increase complexity and lead to unexpected side-effects.
2 Likes

Thank you for the advice!

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.