C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 29:
[29.11] Is it okay if a lot of numbers appear in my code?

Probably not.

In many (not all) cases, it's best to name your numbers so each number appears only once in your code. That way, when the number changes there will only be one place in the code that has to change.

For example, suppose your program is working with shipping crates. The weight of an empty crate is 5.7. The expression 5.7 + contentsWeight probably means the weight of the crate including its contents, meaning the number 5.7 probably appears many times in the software. All these occurrences of the number 5.7 will be difficult to find and change when (not if) somebody changes the style of crates used in this application. The solution is to make sure the value 5.7 appears exactly once, usually as the initializer for a const identifier. Typically this will be something like const double crateWeight = 5.7;. After that, 5.7 + contentsWeight would be replaced by crateWeight + contentsWeight.

Now that's the general rule of thumb. But unfortunately there is some fine print.

Some people believe one should never have numeric literals scattered in the code. They believe all numeric values should be named in a manner similar to that described above. That rule, however noble in intent, just doesn't work very well in practice. It is too tedious for people to follow, and ultimately it costs companies more than it saves them. Remember: the goal of all programming rules is to reduce time, cost and risk. If a rule actually makes things worse, it is a bad rule, period.

A more practical rule is to focus on those values that are likely to change. For example, if a numeric literal is likely to change, it should appear only once in the software, usually as the initializer of a const identifier. This rule lets unchanging values, such as some occurrences of 0, 1, -1, etc., get coded directly in the software so programmers don't have to search for the one true definition of one or zero. In other words, if a programmer wants to loop over the indices of a vector, he can simply write for (int i = 0; i < v.size(); ++i). The "extremist" rule described earlier would require the programmer to poke around asking if anybody else has defined a const identifier initialized to 0, and if not, to define his own const int zero = 0; then replace the loop with for (int i = zero; i < v.size(); ++i). This is all a waste of time since the loop will always start with 0. It adds cost without adding any value to compensate for that cost.

Obviously people might argue over exactly which values are "likely to change," but that kind of judgment is why you get paid the big bucks: do your job and make a decision. Some people are so afraid of making a wrong decision that they'll adopt a one-size-fits-all rule such as "give a name to every number." But if you adopt rules like that, you're guaranteed to have made the wrong decision: those rules cost your company more than they save. They are bad rules.

The choice is simple: use a flexible rule even though you might make a wrong decision, or use a one-size-fits-all rule and be guaranteed to make a wrong decision.

There is one more piece of fine print: where the const identifier should be defined. There are three typical cases:

  • If the const identifier is used only within a single function, it can be local to that function.
  • If the const identifier is used throughout a class and no where else, it can be static within the private part of that class.
  • If the const identifier is used in numerous classes, it can be static within the public part of the most appropriate class, or perhaps private in that class with a public static access method.

As a last resort, make it static within a namespace or perhaps put it in the unnamed namespace. Try very hard to avoid using #define since the preprocessor is evil. If you need to use #define anyway, wash your hands when you're done. And please ask some friends if they know of a better alternative.

(As used throughout the FAQ, "evil" doesn't mean "never use it." There are times when you will use something that is "evil" since it will be, in those particular cases, the lesser of two evils.)