So every number in the code that we are sending to a method as an argument is considered as a Magic Number?
To me, it shouldn’t. I think if some number is let’s say it is for minimum length of user name and we start using “6” in the code…then yeah we have a maintenance problem and here “6” is a magic number….but if we are calling a method that one of its arguments accept an integer for example as the ith member of a collection and then we pass “0” to that method call, in this case I don’t see that “0” as a magic number.
What do you think?
3
If the meaning of the number is very clear in the context, I don’t think it’s a “magic number” problem.
Example: Let’s say you’re trying to get the substring of a string, from the beginning to some token and the code looks like this (imaginary language and library):
s := substring(big_string, 0, findFirstOccurence(SOME_TOKEN, big_string));
In this context, the meaning of the number 0 is clear enough. I suppose you could define START_OF_SUBSTRING
and set it to 0, but in this case I think it would be overkill (although it would be the correct approach if you knew that the start of your substring might not be 0, but that depends on the specifics of your situation).
Another example might be if you are trying to determine if a number is even or odd. Writing:
isEven := x % 2;
is not as strange as:
TWO := 2;
isEven := x % TWO;
Testing negative numbers as
MINUS_ONE := -1;
isNegativeInt := i <= MINUS_ONE;
also feels weird to me, I’d much rather see
isNegativeInt := i <= -1;
11
bool hasApples = apples > 0;
It is obvious zero means absence. I find 0 easier to understand than a variable named “absenceValue”.
for(int i=0; i < arr.length; i++)
It’s obvious 0 is the starting position. I would be confused by a variable named “firstPosition”. Such a variable would make me wonder if the starting position could change.
I would suggest three key factors in deciding whether something should be a constant declaration:
- Is the number something that is precisely and concisely representable
- Are there any plausible scenarios under which the value would have to change, but the code would not have to be rewritten
- Would someone who sees the number be apt to recognize it more quickly or less quickly than someone who sees a named constant
Something like pi should probably be written as a named constant, rather than as a numeric literal, since a numeric literal is apt to be needlessly verbose, needlessly imprecise, or both. Something like the number of slots in a cache should likely be a named constant (though see note below) to allow for the possibility of expanding the cache without having to modify all the code that uses it. Things like the numbers “4”, “28”, and “29” in the statement if ((year % 4)==0) FebruaryDays = 29; else FebruaryDays = 28;
should probably not be named constants, since the expression is almost certainly more readable than if ((year % YearsBetweenLeapYears)==0) FebruaryDays = FebruaryDaysInLeapYear; else FebruaryDays = FebruaryDaysInNonLeapYear;
. Note that the maintainers of standards have indicated that the length of February 2100 in that year will not match the above formula, but I would recommend not handling that case unless it would be the only impediment to correctly handling such dates (i.e. the code won’t get tripped by integer overflow or other such problems).
An important caveat with rule #2 is that in some cases code may rely upon hard-coded numbers in a way which cannot readily be represented by a named constant. For example, a method which computes a cross product of two vectors passed as discrete parameters will only be meaningful when used on three-dimensional vectors. The required number of dimensions is not a value that could meaningfully be changed without completely rewriting the routine. Even if one foresaw a possible need for computing the cross product of three 4-dimensional vectors, using a named constant for the value “3” would do little to make it easier to satisfy that need.
4
This, as all principles, is a matter of degree. Generally speaking, number literals in source code are more suspect the larger they are. A maximum length like 10 or a memory address like 0x587FB0 are obviously bad practice – it is almost certain that sooner or later you will have to repeat these values more than once, creating a risk of incompatibility and subtle errors introduced in places that weren’t changed.
0 is at the other end of the scale; it is still suspect but not quite as much.
Are you using 0 as a sentinel value? Then you should probably use a symbolic constant instead, just because the constant can explain what it means. Is it an extremely entrenched cultural agreement such as “0 means successful completion”? That’s probably OK. Does it mean “the first item in a collection”? That may be harmless, but if there is an alternate method such as first()
I’d probably prefer that.
2
Every unnamed number that’s not immediately obvious from context is a magic number. Its a little silly to define numbers that have meaning that is immediately obvious from context.
In django (python web framework), I may define some database field with a raw number like:
firstname = models.CharField(max_length=40)
middlename = models.CharField(max_length=40)
lastname = models.CharField(max_length=40)
which is clearer (and the recommended practice ) than say
MAX_LENGTH_NAME = 40
...
firstname = models.CharField(max_length=MAX_LENGTH_NAME)
middlename = models.CharField(max_length=MAX_LENGTH_NAME)
lastname = models.CharField(max_length=MAX_LENGTH_NAME)
as I’m unlikely to ever need to change the length (and can always compare to the max_length
of the field). If I do need to change the length of the field after initially deploying the application, I need to change it in exactly one location per field in my django code, and then additionally write a migration to change the schema of the DB. If I ever need to reference max_length
of a defined field of a type of object, I can do it directly — if those fields were defining a Person
class, I can use Person._meta.get_field('firstname').max_length
to obtain the max_length
being used (which is defined in one place). The fact that the same 40 was used for multiple fields is irrelevant as I may want to change them independently. The length of firstname should never depend on the length of middlename or lastname; they are separate values and can change independently.
Often array indices can use unnamed numbers; like if I have a CSV file of data that I want to put in a python dictionary, with the first element in the row as the dictionary key
I would write:
mydict = {}
for row in csv.reader(f):
mydict[row[0]] = row[1:]
Sure I could name index_column = 0
and do something like:
index_col = 0
mydict = {}
for row in csv.reader(f):
mydict[row[index_col]] = row[:index_col] + row[index_col+1:]
or worse define after_index_col = index_col + 1
to get rid of the index_col+1
, but that doesn’t make the code clearer in my view. Also, if I give the index_col
a name, I better make the code work even if the column is not 0 (hence the row[:index_col] +
part).
9
Sometimes numbers in code will never be changed. With integer values, using symbolic names would obscure the code. For example:
radians= (degrees * M_PI) / 180;
and
degrees_fahrenheit= 32 + (degrees_celsius * 9) / 5;
Having code reflect the conversion equation maximizes readability.
I’ve been reading the answers raising my eyebrows. I think all of them, including the Wikipedia page, are wrong.
To me a magic number is something very specific that has nothing to do with whether a constant has been defined for it or not. And I did not make this up myself.
A magic number is the computer science equivalent of in-band signaling. It means there is a number in a domain (say, integers) that gets assigned (semantically) a different meaning than its value. This is always problematic and often a hack to overcome some shortcoming in the programming language, it is an optimization or it’s just a lazy shortcut.
Example: You have a variable Age and you want to abuse it to tell whether the person is still alive or not. This is where you go wrong already but you may be living in the 1960s and be short on memory. You come up with the idea of using 200 to signal someone is dead. That would make 200 a magic number.
In this case it may be obvious there is something about 200 because nobody makes 200 but the potential problems are obvious. You have to be aware of this hack every time you do something with ages. You can no longer just calculate an average age without being aware of it for example.
The classic example of this being a problem is an old AT&T phone system that used a particular frequency not often used in speech to terminate the call. This did not work out well, people whistling over the phone was enough to drop the call.
2
As in everything, context is everything. A couple of simple rules of thumb should be kept in mind: 1) the more frequently it is used, the more likely it is to deserve a name, 2) if the reason why it is that number instead of another number isn’t clear without knowing more than is in the function/method, it deserves a name to explain it.
0 seldom needs a name, because it’s generally clear from context, but there can certainly be circumstances where it’s unclear, where a name or a comment is necessary to explain why it was used. Determining even/odd with a modulo 2 is clear, giving a 50% discount might not be clear whether it is done with a multiplication or a division. Context is everything.
In your specific example, zero would represent the first element of the collection, but you might want to have a name that explains why the first and not the last…