Celeritas
Celeritas

Reputation: 15091

Should unsigned ints be used if not necessary?

Should one ever declare a variable as an unsigned int if they don't require the extra range of values? For example, when declaring the variable in a for loop, if you know it's not going to be negative, does it matter? Is one faster than the other? Is it bad to declare an unsigned int just as unsigned in C++?

To reitterate, should it be done even if the extra range is not required? I heard they should be avoided because they cause confusion (IIRC that's why Java doesn't have them).

Upvotes: 25

Views: 6241

Answers (8)

Andrey Bienkowski
Andrey Bienkowski

Reputation: 1715

juanchopanza recommends you to "use unsigned integers when it doesn't make sense for them to have negative values"..

Been there, done that. Please do not repeat my mistakes. If you use unsigned types for every variable that can never be negative you will soon find yourself:

  1. Subtracting something from an unsigned variable and wanting a signed result
  2. Wanting to perform arithmetic on a signed and an unsigned value

Can it be done? Sure, usually by casting the unsigned value to a signed type, but it is very easy to forget to do the cast - a huge breading ground for bugs.


user541686 recommends unsigned integers because "They [unsigned integers] are more predictable in terms of undefined behavior on overflow and such"

User541686 is referring to the fact that in C and C++ the compiler is allowed to assume that the programmer made sure that signed arithmetic never overflows. Unsigned arithmetic, on the other hand, is guaranteed by the C and C++ standards to wrap-around (UINT_MAX + 1u == 0u)

User541686's advice is wrong because, if there is a bug that results in an overflow, replacing a signed integer with an unsigned one will not fix the bug. At best your program will produce an incorrect output, at worst you may get undefined behavior if your code that uses the result of the calculation relies on the wrap-around not occurring. From the security perspective an incorrect result is not much better than undefined behavior - both may lead to security vulnerabilities.


Ghost2 claims that "the 'unsigned' qualifier gives the compiler more information about the variable, which in turn allows it to squeeze in more optimizations"

Ghost2's claim is incorrect: the use of unsigned may enable some optimizations (I have not verified that), but it will inhibit other optimizations because the use of a signed type tells the compiler that it is allowed to assume the calculation will never overflow (example)


Due to the reasons I explained above the rule of thumb I use is: use signed integers by default, use unsigned integers where wrap-around is expected, intended and desired.

This has the advantage that an unsigned type becomes a strong signal that the code relies on wrap-around for correctness

Upvotes: 1

Pierre
Pierre

Reputation: 31

Even if you have variables that should only take non negative values unsigned can be a problem. Here is an example. Suppose a programmer is asked to write a code to print all pairs of integer numbers (a,b) with 0 <= a < b <= n where n is a given input. An incorrect code is

for (unsigned b = 0; b <= n; b++)
   for (unsigned a=0; a <=b-1; b++)
       cout << a << ',' << b << n ;

This is easy to correct, but thinking with unsigned is a bit less natural than thinking with int.

Upvotes: 3

Ghost2
Ghost2

Reputation: 532

The reason to use uints is that it gives the compiler a wider variety of optimizations. For example, it may replace an instance of 'abs(x)' with 'x' if it knows that x is positive. It also opens up a variety of bitwise 'strength reductions' that only work for positive numbers. If you always mult/divide an int by a power of two, then the compiler may replace the operation with a bit shift (ie x*8 == x<<3) which tends to perform much faster. Unfortunately, this relation only holds if 'x' is positive because negative numbers are encoded in a way that precludes this. With ints, the compiler may apply this trick if it can prove that the value is always positive (or can be modified earlier in the code to be so). In the case of uints, this attribute is trivial to prove, which greatly increases the odds of it being applied.

Another example might be the equation y = 16 * x + 12. If x can be negative, then a multiply and add would be required. Yet if x is always positive, then not only can the x*16 term be replaced with x<<4, but since the term would always end with four zeros this opens up replacing the '+ 12' with a binary OR (as long as the '12' term is less than 16). The result would be y = (x<<4) | 12.

In general, the 'unsigned' qualifier gives the compiler more information about the variable, which in turn allows it to squeeze in more optimizations.

Upvotes: 12

AProgrammer
AProgrammer

Reputation: 52334

The problem with the systematic use of unsigned when values can't be negative isn't that Java doesn't have unsigned, it is that expressions with unsigned values, especially when mixed with signed one, give sometimes confusing results if you think about unsigned as an integer type with a shifted range. Unsigned is a modular type, not a restriction of integers to positive or zero.

Thus the traditional view is that unsigned should be used when you need a modular type or for bitwise manipulation. That view is implicit in K&R — look how int and unsigned are used —, and more explicit in TC++PL (2nd edition, p. 50):

The unsigned integer types are ideal for uses that treat storage as a bit array. Using an unsigned instead of an int to gain one more bit to represent positive integers is almost never a good idea. Attempts to ensure that some values are positive by declaring variables unsigned will typically be defeated by the implicit conversion rules.

Upvotes: 9

Benjamin Lindley
Benjamin Lindley

Reputation: 103751

int is the general purpose integer type. If you need an integer, and int meets your requirements (range [-32767,32767]), then use it.

If you have more specialized purposes, then you can choose something else. If you need an index into an array, then use size_t. If you need an index into a vector, then use std::vector<T>::size_type. If you need specific sizes, then pick something from <cstdint>. If you need something larger than 64 bits, then find a library like gmp.

I can't think of any good reasons to use unsigned int. At least, not directly (size_t and some of the specifically sized types from <cstdint> may be typedefs of unsigned int).

Upvotes: 10

juanchopanza
juanchopanza

Reputation: 227558

You should use unsigned integers when it doesn't make sense for them to have negative values. This is completely independent of the range issue. So yes, you should use unsigned integer types even if the extra range is not required, and no, you shouldn't use unsigned ints (or anything else) if not necessary, but you need to revise your definition of what is necessary.

Upvotes: 8

user541686
user541686

Reputation: 210755

More often than not, you should use unsigned integers.

They are more predictable in terms of undefined behavior on overflow and such.
This is a huge subject of its own, so I won't say much more about it.
It's a very good reason to avoid signed integers unless you actually need signed values.

Also, they are easier to work with when range-checking -- you don't have to check for negative values.

Typical rules of thumb:

  • If you are writing a forward for loop with an index as the control variable, you almost always want unsigned integers. In fact, you almost always want size_t.

  • If you're writing a reverse for loop with an index as a the control variable, you should probably use signed integers, for obvious reasons. Probably ptrdiff_t would do.

The one thing to be careful with is when casting between signed and unsigned values of different sizes.
You probably want to double-check (or triple-check) to make sure the cast is working the way you expect.

Upvotes: 6

knightrider
knightrider

Reputation: 2153

In almost all architectures the cost of signed operation and unsigned operation is the same. So efficiency wise you wont get any advantage for using unsigned over signed. But as you pointed out, if you use unsigned you will have a bigger range

Upvotes: 2

Related Questions