Converting Numbers to Ints
This is an extremely common task: converting a Number
to an int
. There are a lot of ways to do it, but which is fastest in AS3? Today we’ll look at a performance test app that attempts to find the fastest way to accomplish this. The answer just may surprise you!
This is a (probably incomplete) list of ways to convert a converting a Number
to an int
:
- The
int(num)
cast - The
Math.floor
function - Assignment to an
int
-typed variable - Left shift (
<<
) by 0 - Right shift (
>>
) by 0 - Bitwise AND (
&
) with 0xFFFFFFFF - Bitwise OR (
|
) with 0 - Bitwise XOR (
^
) with 0 - Two bitwise NOT (
˜
) operations
So I made a performance test app to try out all of the above conversion methods:
package { import flash.display.*; import flash.utils.*; import flash.text.*; public class NumberToInt extends Sprite { private var __logger:TextField = new TextField(); private function row(...cols): void { __logger.appendText(cols.join(",")+"\n"); } public function NumberToInt() { 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 NUM:Number = 3.5; var REPS:int = 100000000; var valInt:int; var tempInt:int; row("Method", "Time"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = NUM >> 0; } afterTime = getTimer(); row(">>", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = NUM << 0; } afterTime = getTimer(); row("<<", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = NUM & 0xFFFFFFFF; } afterTime = getTimer(); row("&", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = NUM | 0; } afterTime = getTimer(); row("|", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = NUM ^ 0; } afterTime = getTimer(); row("^", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = ~(~NUM); } afterTime = getTimer(); row("~~", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = (tempInt = NUM); } afterTime = getTimer(); row("=", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = int(NUM); } afterTime = getTimer(); row("int()", (afterTime-beforeTime), valInt); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { valInt = Math.floor(NUM); } afterTime = getTimer(); row("Math.floor()", (afterTime-beforeTime), valInt); } } }
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.4.402.265
- 2.3 Ghz Intel Core i7
- Mac OS X 10.8.1
And here are the results I got:
Method | Time |
---|---|
>> | 188 |
<< | 189 |
& | 187 |
| | 187 |
^ | 187 |
~~ | 217 |
= | 278 |
int() | 188 |
Math.floor() | 538 |
Surprisingly, to me at least, the simple int(num)
cast is just as quick as the rest of the fast approaches. The other fast approaches are the single bitwise operations (shifts, AND, OR, XOR). Then comes assignment to an int
-typed variable and the double bitwise NOT, which aren’t much slower. Bringing up the rear is Math.floor
, which returns only a Number
that has its fractional part chopped off and still requires conversion to a true int
-typed variable.
The basic int(num)
cast is, to me, the clearest code to read so it’s very nice to see that it doesn’t involve the performance costs of calling a global int
function like, say, the global isNaN
function. So for the best performance and clarity, keep it simple and use a cast.
Spot a bug? Have another way of converting that you think might be faster? Post a comment!
#1 by Martin on September 3rd, 2012 ·
I love this series of performance articles, but isn’t it time to retest them in ASC2.0? :)
#2 by jackson on September 3rd, 2012 ·
Thanks for the compliment. :)
As for ASC 2.0, I’m really excited to re-test a lot of articles with it, despite the immense effort required. However, I make it a policy to not test beta products. For this reason, I’ve held off doing tests with Flash Player versions in Adobe Labs, even when they have major new features like
Stage3D
. The same goes with ASC 2.0, so I’ll only be switching over to it and re-testing once a final release is available. I’m really excited for the performance improvements!#3 by arotter on September 3rd, 2012 ·
I was about to ask/suggest the same Martin. Since Thibault officially introduced ASC2.0 on ByteArray.org I was wondering how much all those little helpers (faster isNaN(), faster convert to X, faster this, faster that) would benefit from [Inline]. Any chance you’re going to take a look at this? :)
#4 by jackson on September 3rd, 2012 ·
Most definitely, and it’s really exciting! However, the current version is only a preview (a.k.a. beta) on Adobe Labs and I try not to test betas. I’ll be doing my tests (and probably a huge series of re-tests) with ASC 2.0 once it’s out of beta.
#5 by arotter on September 3rd, 2012 ·
Oh well, we must have posted at basically the same time. ^^
Anyway, good to hear that it’s on your radar! :) Someone’s already been running a converted version of these tests https://developers.google.com/octane/benchmark (http://iq12.com/blog/as3-benchmark/) against ASC2 and the results were from +0% to +12%. So far it’s really looking good (especially since even without using inlining ASC2 some tests were getting a speed increase of +6%).
#6 by Thijs on September 4th, 2012 ·
Note that these methods doesn’t work the same for negative numbers:
int(-.5) = 0
Math.floor(-.5) = -1
So if you want to do a good comparison, you should add a check for this as well.
#7 by jackson on September 4th, 2012 ·
Good point. This would make the
Math.floor
approach even slower.#8 by Mark on September 5th, 2012 ·
Math.floor
returns aNumber
, notint
, the function itself can go beyondint
:So that probably shows why it’s slower, you’re not actually converting it to
int
. It looks like this one does not belong in the list if the benchmark is about ints, but when it is about flooring numbers, it should of course.I have no idea what happens internally (maybe optional useful to benchmark), if you don’t convert it yourself, but just use int as type, cause that works too if you want to have
int
.#9 by Mark on September 5th, 2012 ·
Sorry the formatting looks weird because the code was ment to be inline..
#10 by jackson on September 5th, 2012 ·
No problem, I fixed it. Just use <code> blocks for inline code snippets. I’ll update the prompt to mention that, too.
I included
Math.floor
because I’ve seen programmers who believe that it somehow helps in the conversion fromNumber
toint
, but as you can see from the=
test and your final snippet, it’s totally unnecessary. So I agree:Math.floor
shouldn’t ever be used for that purpose.#11 by Rambo on October 17th, 2012 ·
you may try about NUMBER.toFix()
#12 by jackson on October 18th, 2012 ·
I’m not sure what you’re referring to. Number.toFixed() creates a
String
version of theNumber
which may have a fractional part. How does this convert to anint
?#13 by benjamin guihaire on July 9th, 2013 ·
there is a way using alchemy opcode to perform much faster conversions ..
http://guihaire.com/code/?p=156
#14 by Slava on March 15th, 2016 ·
Return a tuple consisting of the two numeric arguments converted to a common type, using the same rules as used by arithmetic operations. If coercion is not possible, raise