Math.abs is a commonly-used utility function for taking the absolute value of a Number. However, there’s no special-case version for taking the absolute value of an int. Of course Math.abs will work for int values, but we can do it faster. Read on for a couple of ways.

The first way to make Math.abs faster is to do the operation yourself. It’s a really simple function and it’ll work for int and Number:

result = input < 0 ? -input : input;

The second way relies on some bitwise magic:

var mask:int = input >> 31;
result = (input + mask) ^ mask;

But the compiler doesn’t do as good a job as it could with constant local variables so let’s try a related way that involves double-computing mask and inlining it:

result = (input + (input >> 31)) ^ (input >> 31);

Now let’s put them to the test with a little performance-testing app:

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class FasterAbs extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function row(...cols): void
		{
			__logger.appendText(cols.join(",")+"\n");
		}
 
		public function FasterAbs()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			init();
		}
 
		private function init(): void
		{
			var beforeTime:int;
			var afterTime:int;
			var i:int;
			var REPS:int = 100000000;
			var absInt:int;
			var absNumber:Number;
 
			row("Method", "Time");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				absInt = Math.abs(i);
			}
			afterTime = getTimer();
			row("int = Math.abs(i)", (afterTime-beforeTime), absInt);
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				absNumber = Math.abs(i);
			}
			afterTime = getTimer();
			row("Number = Math.abs(i)", (afterTime-beforeTime), absNumber);
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				absInt = i < 0 ? -i : i;
			}
			afterTime = getTimer();
			row("int = i < 0 ? -i : i", (afterTime-beforeTime), absInt);
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				var mask:int = i >> 31;
				absInt = (i + mask) ^ mask;
			}
			afterTime = getTimer();
			row("int = (i + mask) ^ mask", (afterTime-beforeTime), absInt);
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				absInt = (i + (i >> 31)) ^ (i >> 31);
			}
			afterTime = getTimer();
			row("int = (i + (i >> 31)) ^ (i >> 31)", (afterTime-beforeTime), absInt);
		}
	}
}

I ran this test app in the following environment:

  • Flex SDK (MXMLC) 4.6.0.23201, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 11.3.300.268
  • 2.3 Ghz Intel Core i7
  • Mac OS X 10.8.0

And here are the results I got:

Method Time
int = Math.abs(i) 502
Number = Math.abs(i) 478
int = i < 0 ? -i : i 242
int = (i + mask) ^ mask 194
int = (i + (i >> 31)) ^ (i >> 31) 194

Faster Math.abs Performance Chart

We can draw some conclusions from these results:

  • Math.abs is better when its result Number doesn’t need to be converted to an int. Unfortunately, this is always the case when you are getting the absolute value of an int.
  • Inlining Math.abs with the simple ternary (? :) operator is about twice as fast as Math.abs
  • The version with the bitwise tricks is the fastest of the three approaches and about 20% faster than the ternary operator version.
  • Both bitwise trick versions (mask and no mask) are about the same. It’s a shame that the use of a local variable is so expensive that it equals double-computing the mask.

So if you’re looking for a way to improve the speed of your integer absolute values, now you can do even better than doubling the performance of Math.abs.

Questions? Suggestions? Spot a bug? Post a comment!