Note: This is the same puzzler that was featured in the March issue of the Adobe Edge newsletter!
It’s about that time again…time for another Puzzler! I’ve written a few before, in a couple of languages too, but this will be my first in ActionScript! I know I have a lot of Flash readers, so hopefully this will be extra ‘puzzling’ for you (see what I did there?). For those that don’t know what a Puzzler is, it is essentially a short problem used to demonstrate special cases or “features” of a particular language. Here is the format:
- Code – I introduce the code
- Question – I pose a multiple-choice question and you guess what the outcome is…think hard!
- Walkthrough – I walk through a reasonable explanation
- Answer – I tell you the real outcome (it might surprise you), and explain why
- Moral – How can we avoid making mistakes like this in our own code
Now that we all know what a Puzzler is, here is a simple ActionScript Puzzler:
// declare key dates
var now:Date = new Date();
var birthday:Date = new Date(1940, 06, 27); // Bugs Bunny’s birthday!
// save them as milliseconds since epoch
var nowInMs:int = now.getTime();
var birthdayInMs:int = birthday.getTime();
// get the difference to calculate Bugs’ age in milliseconds
var millisecondsElapsed:int = nowInMs - birthdayInMs;
// sanity check
if (millisecondsElapsed == now.getTime() - birthday.getTime())
trace("What’s up, Doc?");
What does this print?
- “What’s up, Doc?”
- Throws an exception
- None of the above
Alright, it looks like we are just trying to compute Bugs Bunny’s age in milliseconds, from the time he was “born” until now. First, we create the key dates. We have now initialized to the current date (as in the date this program is executed), and birthday initialized to Bug Bunny’s birthday. We then go and save those times as milliseconds since epoch so that we can easily do some date math with these two dates. Next, we calculate the difference of Bugs’ birthday (in ms) from now (also in ms) which should give us the total milliseconds elapsed since he was born. Finally, we do a sanity check. In the sanity check, we essentially duplicate the math we’ve done previously: calculate the difference from Bugs’ birthday until now, in milliseconds. This should obviously be true, and so we trace “What’s up, Doc?” to the debug console.*SPOILER ALERT – ANSWER BELOW*
The answer is a – nothing! How does this happen? Well, we must be skipping over the trace statement. That would only occur if the sanity check fails. But the sanity check does exactly what is done earlier on in the code…almost! The key error in this code is that there is an implicit narrowing type-cast! Where is it? Let’s take a look at the description of the Date class in the ActionScript 3 LiveDocs. In the documentation for the Date.getTime() method, we get the following function prototype:
Returns the number of milliseconds since midnight January 1, 1970, universal time, for a Date object.
Date.getTime() returns a Number! And we are storing that number in an int. In ActionScript, a Number uses the IEEE-754 double-precision floating-point specification, and therefore can use 53 bits to represent integer values. Comparing this to only 32 bits used by int and uint, we can see that Numbers can be used to represent values well beyond the range of the int and uint data types. Because of this implicit type-cast, our millisecond time gets narrowed and loses precision! Now, knowing this, it is easy to see why our sanity check fails. Our int variable millisecondsElapsed does not, in fact, store the total milliseconds elapsed since Bug Bunny was born, but rather, it stores the total milliseconds elapsed since he was born, truncated to 32 bits. Therefore, the sanity check fails, the if-statement falls through, and the trace statement never gets executed.
Although in some other languages, implicit narrowing type-casts cause warnings or even compilation errors, in ActionScript, they do not. So, whenever you are storing or working with numbers, be aware of the size of the numbers as well as the data-types floating around. Implicit casts do occur (and often, in any language), but implicit narrowing casts will go unnoticed by the compiler, and likely by you as well…until now :)
That’s it for this Puzzler! Hope you enjoyed! Happy coding!
actionscript-puzzler-the-joy-of-millisecs.zip (source code)