JavaScript’s Modulus “Bug”

I recently encountered an issue that caused me a bit of confusion. JavaScript was not calculating a modulus in the same manner that Windows Calculator or Visual Basic was. At first I had thought this to be a bug, but after doing a bit more research I discovered that it was actually giving the correct answer and it was the Microsoft products that were giving me the wrong answer. To help illustrate this issue, open up your Windows Calculator and enter 1.47 MOD 0.05 and then look at the result. Now in your browser’s address bar enter javascript:alert(1.47 % 0.05); and press return. Notice the difference? Windows gives you a nice whole number to work with, and when you crunch the numbers in your head it makes sense.

So what’s the solution? It’s rather simple, actually. Since the issue only pops it’s head up when you’re working with decimals, all you have to do is multiply it by the correct power of 10 and then do your calculations. Once you have a result, divide it again by the same power of 10 and you have the answer. So for example, in Canada lately there’s been a lot of talk about the Royal Mint wanting to stop making pennies and as a result all transactions will be rounded to the nearest 5 cents. So lets look at a function that could be used to round a value down to a 5 cent increment:

   
    // Enter a value in dollars and cents, such as 4.97 or 1.91
    var value = prompt("Please enter an amount","Amount"); 
    var balance_remainder = 0;
    var balance_sign = 1;

    if(isNaN(value)) {
        alert("Not a Number!");
        value = 0;

    } else {
        // NOTE: JS calculates a modulus on floating point numbers differently than some languages                                                                                                         
        //       The solution is to multiply currencies by 100 before getting the modulus and then dividing the answer by 100                                                                              

        // Strip the negative off for now so it doesn't screw with the calculations                                                                                                                        
        if(value < 0) {
            balance_sign = -1;
            value = value * -1;
        } else {
            balance_sign = 1;
        }

        // Calculate the remainder                                                                                                                                                                         
        balance_remainder = (value * 100) % 5;
        balance_remainder = balance_remainder / 100;

        // Strip off the remainder                                                                                                                                                                         
        if(balance_remainder > 0) {
            value = value - balance_remainder;
            value = Math.round(value * 100) / 100;
        }

        // Re-sign the value                                                                                                                                                                               
        value = value * balance_sign;
    }
    
    alert("Result: " + value);

And there you have it!


Tags: , , , ,

  • Del.icio.us
  • StumbleUpon
  • Reddit
  • Twitter
  • RSS

Leave a Reply