Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to use -1 to set all bits to true?

I recommend you to do it exactly as you have shown, since it is the most straight forward one. Initialize to -1 which will work always, independent of the actual sign representation, while ~ will sometimes have surprising behavior because you will have to have the right operand type. Only then you will get the most high value of an unsigned type.

For an example of a possible surprise, consider this one:

unsigned long a = ~0u;

It won't necessarily store a pattern with all bits 1 into a. But it will first create a pattern with all bits 1 in an unsigned int, and then assign it to a. What happens when unsigned long has more bits is that not all of those are 1.

And consider this one, which will fail on a non-two's complement representation:

unsigned int a = ~0; // Should have done ~0u !

The reason for that is that ~0 has to invert all bits. Inverting that will yield -1 on a two's complement machine (which is the value we need!), but will not yield -1 on another representation. On a one's complement machine, it yields zero. Thus, on a one's complement machine, the above will initialize a to zero.

The thing you should understand is that it's all about values - not bits. The variable is initialized with a value. If in the initializer you modify the bits of the variable used for initialization, the value will be generated according to those bits. The value you need, to initialize a to the highest possible value, is -1 or UINT_MAX. The second will depend on the type of a - you will need to use ULONG_MAX for an unsigned long. However, the first will not depend on its type, and it's a nice way of getting the most highest value.

We are not talking about whether -1 has all bits one (it doesn't always have). And we're not talking about whether ~0 has all bits one (it has, of course).

But what we are talking about is what the result of the initialized flags variable is. And for it, only -1 will work with every type and machine.


  • unsigned int flags = -1; is portable.
  • unsigned int flags = ~0; isn't portable because it relies on a two's-complement representation.
  • unsigned int flags = 0xffffffff; isn't portable because it assumes 32-bit ints.

If you want to set all bits in a way guaranteed by the C standard, use the first one.


Frankly I think all fff's is more readable. As to the comment that its an antipattern, if you really care that all the bits are set/cleared, I would argue that you are probably in a situation where you care about the size of the variable anyway, which would call for something like boost::uint16_t, etc.


A way which avoids the problems mentioned is to simply do:

unsigned int flags = 0;
flags = ~flags;

Portable and to the point.


unsigned int flags = -1;  // all bits are true

"Is this a good[,] portable way to accomplish this?"

Portable? Yes.

Good? Debatable, as evidenced by all the confusion shown on this thread. Being clear enough that your fellow programmers can understand the code without confusion should be one of the dimensions we measure for good code.

Also, this method is prone to compiler warnings. To elide the warning without crippling your compiler, you'd need an explicit cast. For example,

unsigned int flags = static_cast<unsigned int>(-1);

The explicit cast requires that you pay attention to the target type. If you're paying attention to the target type, then you'll naturally avoid the pitfalls of the other approaches.

My advice would be to pay attention to the target type and make sure there are no implicit conversions. For example:

unsigned int flags1 = UINT_MAX;
unsigned int flags2 = ~static_cast<unsigned int>(0);
unsigned long flags3 = ULONG_MAX;
unsigned long flags4 = ~static_cast<unsigned long>(0);

All of which are correct and more obvious to your fellow programmers.

And with C++11: We can use auto to make any of these even simpler:

auto flags1 = UINT_MAX;
auto flags2 = ~static_cast<unsigned int>(0);
auto flags3 = ULONG_MAX;
auto flags4 = ~static_cast<unsigned long>(0);

I consider correct and obvious better than simply correct.


I am not sure using an unsigned int for flags is a good idea in the first place in C++. What about bitset and the like?

std::numeric_limit<unsigned int>::max() is better because 0xffffffff assumes that unsigned int is a 32-bit integer.


Converting -1 into any unsigned type is guaranteed by the standard to result in all-ones. Use of ~0U is generally bad since 0 has type unsigned int and will not fill all the bits of a larger unsigned type, unless you explicitly write something like ~0ULL. On sane systems, ~0 should be identical to -1, but since the standard allows ones-complement and sign/magnitude representations, strictly speaking it's not portable.

Of course it's always okay to write out 0xffffffff if you know you need exactly 32 bits, but -1 has the advantage that it will work in any context even when you do not know the size of the type, such as macros that work on multiple types, or if the size of the type varies by implementation. If you do know the type, another safe way to get all-ones is the limit macros UINT_MAX, ULONG_MAX, ULLONG_MAX, etc.

Personally I always use -1. It always works and you don't have to think about it.