Extend Math.round, Math.ceil and Math.floor to allow for precision

Math.round, Math.ceil and Math.floor are very useful functions. However, when using them, I find myself many times needing to specify a precision level. You don’t always want to round to an integer, you often just want to strip away some of the decimals.

We probably all know that if we have a function to round to integers, we can round to X decimals by doing Math.round(num*Math.pow(10,X)) / Math.pow(10,X). This kind of duck typing can get tedious, so usually, you roll your own function to do that. However, why not just add that extra functionality to the functions that already exist and you’re accustomed to?

Let’s start with Math.round. It’s the most needed one anyway.

Firstly we’ll have to store the native function somewhere, since we’re going to replace it. So we do something along the lines of:

Math._round = Math.round;

Now let’s *sigh* replace the native Math.round with our own:

Math.round = function(number, precision)
{
	precision = Math.abs(parseInt(precision)) || 0;
	var coefficient = Math.pow(10, precision);
	return Math._round(number*coefficient)/coefficient;
}

And guess what? It still works the old way too, so your old scripts won’t break.

So now, let’s go to Math.ceil and Math.floor. If you notice, the only thing that changes is the function name. Everything else is the same. So, even though we could copy-paste the code above and change the names, we would end up with triple the size of the code that we need and we would have also violated the DRY principle. So we could put the names of the functions in an array, and loop over it instead:

(function(){
	var MathFns = ['round', 'floor', 'ceil' ];
	for(var i = MathFns.length; i>-1; i--)
	{
		Math['_' + MathFns[i]] = Math[MathFns[i]];
		Math[MathFns[i]] = function(number, precision)
		{
			precision = Math.abs(parseInt(precision)) || 0;
			var coefficient = Math.pow(10, precision);
			return Math['_' + MathFns[i]](number*coefficient)/coefficient;
		}
   }
})();

Why the closure? To allow us to be free in defining our variables without polluting the global namespace. In case Array.prototype.forEach() was cross-browser or if you have mutated the Array prototype to add it for non-supporting ones, you could easily do that:

['round', 'floor', 'ceil' ].forEach(function(funcName){
	Math['_' + funcName] = Math[funcName];
	Math[funcName] = function(number, precision)
	{
		precision = Math.abs(parseInt(precision)) || 0;
		var coefficient = Math.pow(10, precision);
		return Math['_' + funcName](number*coefficient)/coefficient;
	}
});

No closures and much easier to read code.

However, nothing comes without a cost. In this case, the cost is performance. In my tests, the new function takes about twice the time of the native one. Adding a conditional to check if the precision is falsy and use the native function directly if so, doesn’t improve the results much, and it would slow the function down for precision values > 0. Of course the speed would be just as much if the function was a normal one and not a replacement for Math[something], that doesn’t have anything to do with it.

  • Instead of storing standard Math.round to Math._round, why not hide it completely inside a closure:

    Math.round = (function() {
      var oldRound = Math.round;
      return function(number, precision) {
        precision = Math.abs(parseInt(precision)) || 0;
        var coefficient = Math.pow(10, precision);
        return oldRound(number*coefficient)/coefficient;
      };
    })();
  • Thanks for commenting Rene and welcome to my blog!

    Good point! Your solution is much better.

    I came up with this “trick” a long time ago and I wasn’t really comfortable with closures at the time. I never really revised it since then. I’ll do it your way from now on 🙂

  • alexiers

    I’m still learning javascript. Mind if I ask some help?

    You see, I want to change a value with multiple decimal to just two.
    Let’s say the value is 3.6789 and I just want to round it up to the first two decimal like 3.68.

    How can it be done? I tried using math.round and ceil and floor but the values end up becoming integers instead.

    Would be a big help if you could.

  • @alexiers: This is precisely what this post is about 😉 You can just use the code I posted, or the one Rene Sarsoo posted and then you can do what you want by inputting the decimals as a second parameter. For instance Math.round(3.6789, 2) would return 3.68 after that.

  • Ashok Yadav

    Above is not perfect try below one

    amtTotal=function(num1, num2, pre){
    var deci1,deci2,floati1,floati2,precision,decitotal=0,floatitotal=0.0;
    var coefficient;
    //get integer part of number like 10.54 as 10
    deci1=parseInt(num1);
    deci2=parseInt(num2);

    //get float part of number like 10.54 as .54 when it has otherwise 0
    floati1=(deci1==parseFloat(num1) ? 0 : Math.abs(parseFloat(num1) – deci1));
    floati2=(deci2==parseFloat(num2) ? 0 : Math.abs(parseFloat(num2) – deci2));

    //calculate decimal places
    precision = Math.abs(parseInt(pre)) || 0;
    coefficient = Math.pow(10, precision);

    //get integer part total
    decitotal+=deci1+deci2;

    //get float part total with decimal places
    floatitotal+=parseFloat(‘0.’+Math.round((floati1+floati2) * coefficient));

    return (decitotal+floatitotal);

    }

    }

  • Ashok Yadav

    One correction in previous function and the new one is below:

    amtTotal=function(num1, num2, pre){
    var deci1,deci2,floati1,floati2,precision,decitotal=0,floatitotal=0.0;
    var coefficient;
    //get integer part of number like 10.54 as 10
    deci1=parseInt(num1);
    deci2=parseInt(num2);

    //get float part of number like 10.54 as .54 when it has otherwise 0
    floati1=(deci1==parseFloat(num1) ? 0 : Math.abs(parseFloat(num1) – deci1));
    floati2=(deci2==parseFloat(num2) ? 0 : Math.abs(parseFloat(num2) – deci2));

    //calculate decimal places
    precision = Math.abs(parseInt(pre)) || 0;
    coefficient = Math.pow(10, precision);

    //get integer part total
    decitotal+=deci1+deci2;
    //get float part total with decimal places
    floatitotal+=parseFloat((Math.round((floati1+floati2) * coefficient))/coefficient);

    return (decitotal+floatitotal);
    }

    }

  • Pingback: Augmenting JavaScript Core Objects Revisited - SitePoint()

  • Pingback: Augmenting JavaScript Core Objects Revisited – Supreme #WordPress Blog | Supreme Factory()

  • Pingback: Augmenting JavaScript Core Objects Revisited | tutfork()

  • Dave

    You have a typo there I guess:

    Math.round(num*Math.pow(X,10)) /Math.pow(X,10)

    should be:

    Math.round(num*Math.pow(10,X)) /Math.pow(10,X)

  • Very useful nowadays especially that there are many people who wanted to have that kind of program that will surely lead them into a very desirable result.

  • Pingback: matka()

  • Pingback: angara fahise()

  • Pingback: angara fahise()

  • Pingback: http://www.theopulenceforlife.com/()

  • Pingback: jumpstartyourwealthgenereview.org()

  • Pingback: Term Life Insurance()

  • Pingback: airport taxi service()

  • Pingback: subways surfers()

  • Pingback: hampton bay ceiling fan replacement parts()

  • Pingback: https://tribot.org/forums/forum/128-premium-scripts/()

  • Pingback: handmade sofa essex()

  • Pingback: Kalyan Matka()

  • Pingback: swing amateur()

  • Pingback: Live draw hongkong()

  • Pingback: cricket-games.co.in/()

  • Pingback: hugo 2015 results()

  • Pingback: life insurance claim lawyer attorney law firm()

  • Morper

    A more precise work is what we craved to achieve and it would now be easy especially if a person would know on how to properly apply this into their work specifically in programming.