Faster Number rounding with bitwise operators in AS3.

as3logo
Time and time again, AS3 reaffirms its place in my heart as one of the most well planned, well implemented and functional languages I’ve had the pleasure of using. So when I came across this little gem – someone trying to perform a bitwise shift left on a floating point number – I wasn’t sure whether to be intrigued, impressed or confused.

var Y:Number = 5.5; 
var X:int = Y << 8; 

I found myself asking why this functionality was included, what would be the outcome, why it worked, and if it did work what sort of rounding  was going on behind the scenes.(Also why would anyone attempt this, but that was fairly tertiary.)

As it turns out, AS3’s SHL operator can actually be used as a very fast and quite useful form of rounding. Read on for an explanation, some examples and calculated times. 

var Y:Number = 6.6;
var X:int = 0;
X =  Y << 8;

If we look at the integer 6, it’s stored in memory as 0x 00 00 00 06, and shifting this left 8 bits will produce  0x 00 00 06 00.  That’s the expected result. All is peachy.

Now look at 6.0 stored as a floating point Number. 0x 40 C0 00 00 Shifting this left is only going to produce  0x C0 00 00 00, which is -2.0.

This lead me to believe that not only was this rounding enforced prior to shifting, but that it would be likely be handled natively. (Or at the very least, at a bytecode level, omitting the extra calls and jumps which calling a library function would incur).

Roll on an hour of programming a test bed and a year of procrastinating, and here are the results including the operations on negative values. In each case, a Number is being converted to an Integer.

  1. Math.floor(6.4) = 6
  2. Math.ceil(6.4) = 7
  3. Math.round(6.4) = 6
  4. int(6.4) = 6
  5. (6.4) << 0  = 6
  6.  
  7.  
  8. Math.floor(6.6) = 6
  9. Math.ceil(6.6) = 7
  10. Math.round(6.6) = 7
  11. int(6.6) = 6
  12. (6.6) << 0  = 6
  13.  
  14.  
  15. Math.floor(-6.4) = 7
  16. Math.ceil(-6.4) = 6
  17. Math.round(-6.4) = 6
  18. int(-6.4) = 6
  19. (-6.4) << 0  = 6
  20.  
  21.  
  22. Math.floor(-6.6) = 7
  23. Math.ceil(-6.6) = 6
  24. Math.round(-6.6) = 7
  25. int(-6.6) = 6
  26. (-6.6) << 0  = 6

 

 

As you can see, Math.floor, ceil and round behave as expected, even with the negative values. The most interesting part for me at least though is the way int() and <-6.4 is rounded down to -6, but -6.6 is also.

It’s almost like it does  X = Math.floor(Math.abs(Y)) * Sign(Y), but ridiculously fast.
How fast? Have a wee looksee.

  1. Math.floor
  2. Time elapsed is 2548ms
  3.  
  4.  
  5. Math.Ceil
  6. Time elapsed is 2521ms
  7.  
  8.  
  9. Math.round
  10. Time elapsed is 2550ms
  11.  
  12.  
  13. Int()…
  14. Time elapsed is 1303ms
  15.  
  16.  
  17. << 0...
  18. Time elapsed is 1234ms

 

That’s right.  n << 0 is a good 5% faster than Int(), and about twice as fast overall as any of the math libraries.

Practical uses?
Well, obviously, it’s not always going to be the right form of rounding for every particular task, but it’s come in handy a few times, especially for example in games with positive X/Y cartesian world coordinates, or mirroring where flooring 6.4 units ahead would produce different results from flooring 6.6 units behind your character.

If anyone finds a really nice application for this, I’d love to hear about it! 

 Here’s the source if you’re interested. Don’t be too harsh, I wrote it before breakfast =)
http://sicklebrick.com/wp-downloads/bitwisemath.as
 

Leave a Reply

Your email address will not be published. Required fields are marked *