Andrew Koenig

Dr. Dobb's Bloggers

The Case Against int, Part 2: Why Signed Integers?

March 02, 2012

Unsigned integer arithmetic is often more useful than signed.

Last week, I argued that the int type was often dangerous because its capacity depends on the implementation. In addition to its implementation-defined precision, int has another drawback, namely that it is signed. As it turns out, unsigned integer arithmetic is often more useful than signed.

For example, a common use of integers is to count things: the number of students in a course, cars in a parking lot, records in a file, and so on. In general, counters cannot be negative; so unsigned types are often more appropriate than signed integer types such as int. Moreover, one common use of such integers is as array or container indices or sizes. For built-in data structures, C++ provides the unsigned type size_t and the corresponding signed type ptrdiff_t; moreover, each library container type has its own size_type that has room for the number of elements in the largest container of each type. So for counting things that can fit in memory, you are probably better off using size_t or container::size_type than you are in using int, or even unsigned. For counting items in files, unsigned long is probably right.

Integers are also commonly used to deal with external quantities. These can be imported from elsewhere, such as numbers that appear in data files, or they can represent things in the physical world that naturally come in integer form, such as dates. In such cases, it is often possible to put bounds on the values that these quantities can have.

If you know these bounds, you know whether a signed or unsigned type is appropriate to deal with them. What you don't know is which specific. One reader noted that C++ has types such as int32_t, which let you specify a specific number of bits in an integer. However, this specification isn't as useful as it looks at first.

For example, suppose we want to be able to deal with signed integers up to 1000000. If we happen to be running on a machine with 24-bit integers, we can fit such an integer in an int. In fact, we can do so even if int is 21 bits, including sign; but there's no int21_t type available for the purpose.

What is available is code like this:

typedef decltype(1000000) million_int;

This code takes advantage of the decltype feature of C++11 to determine the type of the literal 1000000. That type, in turn, is defined to be the first of int, long, and long long that has enough room to contain this particular value on this implementation. It is then possible to use million_int wherever a signed integer type is needed that has room for 1000000.

We can now list our recommendations:

If you are counting array elements, use size_t, or its corresponding signed type ptrdiff_t.

If you are counting container elements, use container::size_type or the corresponding signed type container::difference_type.

If you are counting data in a file, use unsigned long.

If you want to work with integers within a known range, use decltype on a literal to determine the corresponding type.

So far, I haven't come up with a similarly succinct recommendation for when to use int.

Next week, we'll look at some of the hazards of combining signed and unsigned numbers.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!