37
. You have no idea what that number is, do you? A number without context nor a label is a random value. It doesn't tell us anything. Imagine walking by a billboard, with a picture of a person on a boat, and the text is a giant number 89
. I'd be intrigued, but utterly confused. We rightfully reject meaningless numbers...
...so why then do they appear so often in source code? Any time a constant appears in an expression, like 12
, we call it a magic number. Through some dark craft, they arise out of the ether and populate our code. We don't know where they came from, what they mean, or how they got there.
Though I don't go down to the level of this article, my book has a chapter about interviewing as an essential programmer skill. Something simple like fixing magic numbers can leave a positive impression on the interviewer.
An example of magic
When I conduct interviews, I ask the candidate to create a deck of cards. I let them simplify it, by using sequentially numbered cards, instead of suits and ranks. In the majority of code I'm presented a loop of this form:
for i in 0..52:
cards.add( i )
The number 52
doesn't convey any information. Only because we're talking about a card game, and the candidate has assumed a standard poker deck, does the number 52
appear. There are of course many card games that don't have 52 cards.
I request they deal the cards out to multiple players. I accept splitting the deck as well. Frequently, I get code like this:
for i in 0..26:
player1.add( cards[i] )
for i in 26..52:
player2.add( cards[i] )
It's not immediately obvious that this is splitting the deck in half. I have to reconstruct that in my head. Making a mistake while writing is easy. What if instead, you saw this code:
for i in 0..27:
player1.add( cards[i] )
for i in 28..51:
player2.add( cards[i] )
Is the code accounting for some inclusive/exclusive end condition? Is there a bunch of off-by-one errors in there? What is 51
? It doesn't line up with any number I know at all, not even about cards.
Magic number
In this code:
for i in 0...52:
The 52
is known as a magic number. A future coder has no information about what it is. These types of numbers convey no information as to their purpose.
It's easy to get rid of them. Give the numbers a name.
num_cards_in_deck = 52
for i in 0..num_cards_in_deck:
We've immediately improved the quality of this code. A reader knows what the number 52
means. The loop now logically does something for every card in a deck.
Giving a name to a number removes its magical quality. Here we've done it beside the code, but typically symbols like num_cards_in_deck
end up as global constants. They are context-free facts. If multiple pieces of code need the same value, then create one constant and share it.
Another response to the dilemma is to add a comment, such as
# create a standard size deck
to the loop. Just say no to this! Comments are a tool of last resort. They have no structure, can't be enforced by the compiler, and inevitably become out-dated. Using proper symbol names is superior -- code trumps prose.
With our new constant, we can change the second loops:
for i in 0..(num_cards_in_deck/2):
player1.add( cards[i] )
for i in (num_cards_in_deck/2)..num_cards_in_deck:
player2.add( cards[i] )
Again, the quality of the code has improved considerably. These loops now have meaning; it's easy to see we're treating the deck as two halves. It's also much less likely to have an error as we're letting the compiler do the division. Moreover, if the number of cards changes, this loop is still correct.
I begrudgingly use this example only because it comes up in my interviews. These two loops are still bad. From a defensive programming standpoint, you shouldn't use a constant value at all. You should use len(cards)
and len(cards)/2
. Many magic numbers can be removed in this fashion. Rather than tying your code to an arbitrary value, base it on some other knowledge you already have. In this case, we have a cards
collection which has all the information we need.
Put the wand down
Not all constants are magic numbers though. There are some special cases, in particular, 1
. Adding 1
to a number creates its natural successor. There'd be no confusion about what is happening. There are more, but without seeing the code in particular, I don't wish to give general exceptions.
Even a number like 2
may not seem magical, but can nonetheless be improved upon. In the example I've given, it's clear that it splits something in half, but it doesn't convey what half means. It'd be better to say num_players
in that example. Many times, even if the number is obvious, it helps to give it a name. They add clarity to the code.
As fun as magic may be, it's time to put the want down and stop conjuring these numbers into our code.
Read my book What is Programming? and learn what it takes to be a great programmer. I look at the People, the reason software exists, the code at the heart of that software, and you, the person behind the keyboard.
Addendum: A better dealing loop
The dealing loops shown above are still wrong in my opinion. Though not related to magic numbers anymore, here is code that does the dealing between players in a cleaner fashion. It avoids duplicate loops.
for i in len(cards):
player_cards[i % num_players].add( cards[i] )
Another possible option, which closer mimics dealing, and avoids ranges entirely. It also uses an OOP player that contains cards. This type of code is suitable when you need to model all the states visually, or when there is partial dealing involved. It retains intermediate states.
cur_player = 0
while len(cards) != 0:
player[cur_player].cards.add( cards.pop() )
cur_player = (cur_player + 1) % num_players
Top comments (25)
Story from a friend.
Code was outsourced offshore, because that would save money (it didn't). The company had a rule, and tooling I expect, against magic numbers. So the creative little buggers did this:
That's not creative, that's "clever".
I frown on clever coding.
I wonder if other variables were named
iii
,iv
,v
, etc?the
ii
name was my own addition. I was under the impression this was fairly common for indexed array iteration.I don't think I've seen it before. I might have blocked out the memory. If I need a generic variable I just use
i
, orj
, but I prefer giving them better names when possible, likerow
, orx
, oritem_ndx
.Oh,
ii
is often used rather thani
for iterators because it's easier to text search. Then again if you have to search for the iterator you might be doing something wrong.I don't think I've ever searched for an iterator variable.
Is that ... a spread operator?
nah, [...] is often used to indicate that some part was cut from a quote. I guess it doesn't translate well to code blocks.
I often do three vertical dots for this:
If a magic number is hard to name, then a comment explaining how it works might be needed too.
And if there's a true magic number that you don't know why works, then you probably shouldn't have it at all.
I'm okay with a comment on the constant, but not where it is used. Sometimes numbers come from external calculations that are not done in the code. This is common for many formulas in math, science, finance, and graphics.
I've had comments before referring to the code used to calculate the constant. I did this when it was too costly to calculate each time.
One feature I can't wait for more languages to implement, is compile time code execution. Then such costly functions could be visibly coded, but also not have to run each time the program is opened.
Does anything other than C++ offer this now?
I've usually resorted to a pre-build step that modifies source files.
I think I read that scala has it? Personally I'm pretty excited for JAI (though that might not be released for a few years), but that may be mostly suited for game-development rather than general programming and software.
Whenever I hear magic number I always think of the quake fast inverse square root. en.m.wikipedia.org/wiki/Fast_inver...
That was an awesome read.
For square root fun, also see my article Calculating square root using Newton’s iterative method
To read more on the subject, you could to the eslint page about magic numbers.
It's written for Javascript, but you should consider this rule for any language.
I'm not so happy with their detectObjects rule. I like grouping my constants into objects, but they seem to encourage making them all static globals. I guess it's configurable though.
The default value for "detectObjects" seems to be
false
, meaning it will allow you to put magic numbers inside objects (like you do). You don't have to activate the rule, and I think constants under "enums" is a great thing.Omg! Second time coming across a counting cards question...
And started thinking I ought to know card games.
I think the takeaway here is simplifying a problem and making semantic vars.
But if I understand the problem clearly I can apply a set of rules without thinking about card game rules per se, but the interviewer would need to supply the back stories such as suits and how one wins...
"In a row?"
Absolutely. It was amazing.
Magic numbers are pure evil! :D. If you use them and revisit your code in 3 months, you won't remember what were they for.
Sounds like Do you recognize your code 6 months later?