Javascript can't do math, and it's messing up my cash register

Oh man, this is a lot to explain. So I was working on the final algorithm in the javascript section, the cash register. I got to a point where the math was doing weird things it definitely shouldn’t be doing. So I read up on how Javascript is dumb when it comes to completing operations with decimals. I read about that here:

http://adripofjavascript.com/blog/drips/avoiding-problems-with-decimal-math-in-javascript.html

Basically the advice in this article, if working with money anyway, is to multiply things by 100 before doing calculations, and then divide them by 100 in the end. You’re converting the values into whole numbers, doing the operations needed, and then dividing by 100 to make them back into money format (with two decimal places). So I was annoyed I’d have to rewrite a bunch of code, but at least I knew how to fix it.

That is, until I realized that doesn’t always work… So I come to a point in my code where I’m trying to count out quarters for change (I’ve put in comments to hopefully make it easy to find this, but it’s line 78), and I need to turn the number 16.74 into a whole number. But multiplying by 100 doesn’t work. It returns some giant decimal (1673.99999999998) instead of 1674 like it should. I don’t know what to do. Can you help?

Here is the link to the challenge I’m working on:
Cash Register

And here is my code. It’s probably overly long and complicated, and I’m not done. I’m sure the fcc solution is much more elegant, but I think I can figure out all the other things. It’s just this incorrect math stuff I need help with. If I can get javascript to do math correctly, I can totally finish writing this algorithm on my own.

function checkCashRegister(price, cash, cid) {
//Where price is the total the customer must pay, cash is the cash they pay with, and cid defines the amount of money in the drawer. 
  let cashBack = cash - price;
  let dollars = Math.floor(cashBack);
  let drawer = cid;
  let hundredsDrawer = drawer[8][1];
  let twentiesDrawer = drawer[7][1];
  let tensDrawer = drawer[6][1];
  let fivesDrawer = drawer[5][1];
  let onesDrawer = drawer[4][1];
  let quartersDrawer = drawer[3][1];
  let dimesDrawer = drawer[2][1];
  let nickelsDrawer = drawer[1][1];
  let penniesDrawer = drawer[0][1];
  let change = []
  let result = {
    status: "OPEN",
    change: []
  };
  //Generate the correct amount of change to give the customer, or else return "Insufficient funds. "
  while (cashBack > 0) {
    //ONE HUNDRED:
    if (dollars >= 100 && hundredsDrawer >= 100) {
      let hundredsNeeded = Math.floor(dollars / 100) * 100;
      if (hundredsDrawer >= hundredsNeeded) {
        change.push(['ONE HUNDRED', hundredsNeeded]);
        dollars -= hundredsNeeded;
        hundredsDrawer -= hundredsNeeded;
        cashBack = ((cashBack * 100) - (hundredsNeeded * 100)) /100;
    } else {
        change.push(['ONE HUNDRED', hundredsNeeded]);
        dollars -= hundredsDrawer;
        hundredsDrawer = 0;
        cashBack = ((cashBack * 100) - (hundredsDrawer * 100)) /100;
    }
    //TWENTY: 
  } else if (dollars >= 20 && twentiesDrawer >= 20) {
      let twentiesNeeded = Math.floor(dollars / 20) * 20;
      if (twentiesDrawer >= twentiesNeeded) {
        change.push(['TWENTY', twentiesNeeded]);
        dollars -= twentiesNeeded;
        cashBack = ((cashBack * 100) - (twentiesNeeded * 100)) /100;
        twentiesDrawer -= twentiesNeeded;
    } else {
        change.push(['TWENTY', twentiesDrawer]);
        dollars -= twentiesDrawer;
        cashBack = ((cashBack * 100) - (twentiesDrawer * 100)) /100;
        twentiesDrawer = 0;
    }
    //TEN:
  } else if (dollars >= 10 && tensDrawer >= 10) {
      let tensNeeded = Math.floor(dollars / 10) * 10;
      if (tensDrawer >= tensNeeded) {
        change.push(['TEN', tensNeeded]);
        dollars -= tensNeeded;
        cashBack = ((cashBack * 100) - (tensNeeded * 100)) /100;
        tensDrawer -= tensNeeded;
    } else {
        change.push(['TEN', tensDrawer]);
        dollars -= tensDrawer;
        cashBack = ((cashBack * 100) - (tensDrawer * 100)) /100;
        tensDrawer = 0;
    }
    console.log(cashBack);
    //FIVE:
  } else if (dollars >= 5 && fivesDrawer >= 5) {
      let fivesNeeded = Math.floor(dollars / 5) * 5;
      if (fivesDrawer >= fivesNeeded) {
          change.push(['TEN', fivesNeeded]);
          dollars -= fivesNeeded;
          //
          //
          //
          //The line below this is where the problem is. With the current arguments passed into the function (the ones I console logged out below), cashBack is currently 16.74. But when 16.74 gets multiplied by 100 in the line below, it returns NOT 1674, but rather 1673.99999999998. So once that happens, none of the rest of the math works. 
          //
          //
          //
          cashBack = ((cashBack * 100) - (fivesNeeded * 100)) /100;
      } else {
          change.push(['FIVE', fivesDrawer]);
          dollars -= fivesDrawer;
          cashBack = ((cashBack * 100) - (fivesDrawer * 100)) /100;
          fivesDrawer = 0;
      }
      //ONE:
  } else if (dollars >= 1 && onesDrawer >= 1) {
      let onesNeeded = dollars;
      if (onesDrawer >= onesNeeded) {
          change.push(['ONE', onesNeeded]);
          dollars = 0;
          cashBack = ((cashBack * 100) - (onesNeeded * 100)) /100;
          onesDrawer -= onesNeeded;
      } else {
          change.push(['ONE', onesDrawer]);
          dollars -= onesDrawer;
          cashBack = ((cashBack * 100) - (onesDrawer * 100)) /100;
          onesDrawer = 0;
      }
      //QUARTER:
  } else if (cashBack >= 0.25 && quartersDrawer >= 0.25) {
      //Escape js division problems by working with whole numbers.
      let quartersNeeded = (cashBack * 100)  / 25; //This is a whole number, representing the number of quarters needed, not their value.
      if (quartersDrawer >= quartersNeeded * 0.25) {
          change.push(['QUARTER', quartersNeeded * 0.25]);
          //
          //
          //You'll need to fix the line below probably.
          //
          //
          cashBack = ((cashBack * 100) - (quartersNeeded * 0.25)) / 100;
          quartersDrawer -= (quartersNeeded * 0.25);
      } else {
          change.push(['QUARTER', quartersDrawer]);
          //
          //
          //You'll need to fix the line below probably.
          //
          //
          cashBack = (cashBack * 100 - quartersDrawer) / 100;
          quartersDrawer = 0;
      }
      //DIME:
    } else if (cashBack >= 0.10 && dimesDrawer >= 0.10) {
      //Escape js division problems with whole numbers. 
      let dimesNeeded = (cashBack * 100) / 10;
        if (dimesDrawer >= dimesNeeded * 0.10) {
            change.push(['DIME', dimesNeeded * 0.10]);
          //
          //
          //You'll need to fix the line below probably.
          //
          //
            cashBack = ((cashBack * 100) - (quartersNeeded * 0.10)) / 100;
            dimesDrawer -= (dimesNeeded * 0.10);
        } else {
            change.push(['DIME', dimesDrawer]);
          //
          //
          //You'll need to fix the line below probably.
          //
          //
            cashBack = (cashBack * 100 - dimesDrawer) / 100;
            dimesDrawer = 0;
        }
        //NICKEL:
    } else if (cashBack >= 0.05 && nickelsDrawer >= 0.05) {
      //Escape js division problems with whole numbers. 
      let nickelsNeeded = (cashBack * 100) / 5;
        if (nickelsDrawer >= nickelsNeeded * 0.05) {
            change.push(['NICKEL', nickelsNeeded * 0.05]);
          //
          //
          //You'll need to fix the line below probably.
          //
          //
            cashBack = ((cashBack * 100) - (quartersNeeded * 0.05)) / 100;
            nickelsDrawer -= (nickelsNeeded * 0.05);
        } else {
            change.push(['NICKEL', nickelsDrawer]);
          //
          //
          //You'll need to fix the line below probably.
          //
          //
            cashBack = (cashBack * 100 - nickelsDrawer) / 100;
            nickelsDrawer = 0;
        }
    } else if (penniesDrawer >= cashBack) {
        let penniesNeeded = cashBack;
        change.push(['PENNY', cashBack]);
        cashBack = 0;
        drawer[0][1] = drawer[0][1] - cashBack;
    } else {
        cashBack = 0; //You put this to fix the endless loop. You might not keep this?
      //Something that returns the INSUFFICIENT_FUNDS thing. 
    }
  } 
  //May an if statement to adjust the status to "closed" if you are out of funds. 
  //Erase what's below this, and instead return the result object with the status and the change for the customer.
  return change;
}



console.log(
// 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]]),
// '\n',
'\n',
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]])
);

Probably converting to string and then to number would be an option:

+dollars.toFixed(2).replace('.', '')

It’s not only in JavaScript, but in general calculations on floating points numbers can be imprecise.

Consider that despite of multiplying and dividing by 100 some calculations in the function are still done on the floats. Try to figure out a way to avoid that all together. This means at the start making sure all needed values are represented by integers and only at the end (if needed) change them back to floats, keeping all intermediate results also as integers.

1 Like

Wow you’ve written alot of code. When working with numbers i would recommend calling toFixed on them to make your life easier. Some tips to nudge you in the right direction

  1. Divide your tasks into functions not if/else statements. Like having function to check if your totalCash is enough for change.
  2. Create an object to store the different curriencies and their corresponding value(not your cid)
  3. Nesting a while loop inside a for loop will do wonders for you
  4. I don’t want to spoil the challenge for you but having variables store some data for you like how many times a currency was given will be a game changer.

I’ve tip toed alot to not spoil things but hope you will figure things out with these tips.

do Math.round(number*100), in that way you are sure to have whole numbers

1 Like

You should keep the values as numbers of cents (i.e. multiplied by 100) until the very end. Multiplying and dividing on each step does not help overcome floating point roundoff.

1 Like

I really appreciate you not spoiling it. It’s the last one, and I really think I can get it. But I do have questions.

  1. This sounds like it would be a lot easier to read than all those if statements, but I need an example or a detailed explanation to make it happen. Can you suggest any places to read how to do that? I learn well from reading. Or maybe could you show me an example, but not with the cash register problem, just some random if statement and how one could instead write it as a function? Sorry if that’s a dumb question. Js is my first programming language. I just started the fcc curriculum a little less than 2mo ago, and I didn’t really know much to begin with.

  2. But why? I don’t understand why you suggest this. Can you explain why this would be useful?

  3. I can do that if I go play with things. That could fix some of the nested if loops that make it so hard to read.

  4. I think I can do this too.

Going to go read about toFixed. I haven’t messed with this since I posted it late last night. Hopefully I’ll make a little progress tonight. This one is so much harder than all the other ones! The roman numerals and the palindrome and stuff all took like 20min - 3hr. I think I’m on like day 3 with this silly cash register.

It’s a very difficult problem and i won’t lie, when i first attempted it I did exactly what you are doing now lol. Here’s some clarification:

  1. I suggested you take the functional approach because you effectively divide your code into little tasks that work hand-in-hand to complete a whole without complicating everything. e.g Having a little function that calculates how much is in your drawer total can be helpful.
  2. Creating the currency object that stores their actual value not your cid will help you when you write the function to calculate change to return. spoiler e.g diving the amount of a currency you have by its value will let you know how much of that currency you can give out and you can have a variable track how many times that currency was given out before it exceeded the change needed and moves to the next currency. This is where that nested while loop in a for loop comes in handy

In general you’re gonna have to be real creative to solve this problem. I would recommend if you find it too hard to move to the next section and keep coding and you can revisit it another time.