Rouge one : a story of missing constants

Last time we saw how integer values are represented in memory, this time let’s shift some bits around and compute constant values because it’s more fun. I will continue to stick to the 32-bit integers for the rest of this article. After all, how different are they from 64-bit integers anyway!

Suppose the .NET team decided to drastically cut down on compiled assembly sizes, and int.MaxValue, int.MinValue, uint.MaxValue and uint.MinValue constants have mysteriously disappeared from BCL. How can we compute those ourselves?

Let’s start with the simplest one. uint.MinValue is obviously 0 (represented in binary as all zeros). One down, three to go!

uint stores an unsigned integer and the most significant bit (MSB) means exactly the same as every other bit. So all we need to do to get the largest possible value we can store is to simply flip all its bits from 0 to 1.

To do so we will employ the ~ unary operator. As per documentation, this operator produces a bitwise complement of its operand by reversing each bit.

uint.MaxValue == ~0U

Note that we need an unsigned literal 0U to make compiler happy.

Now to the fun part. As we remember from previous article, signed integer sign is determined by the MSB. We just need to get rid of the MSB in uint.MaxValue to obtain int.MaxValue. We do that by shifting all the bits of uint.MaxValue to the right by using >> binary operator.

int.MaxValue == (int)(uint.MaxValue >> 1)

Note the typecast to keep compiler nice and happy. Splendid, just one to go!

It may look like that all we need to do is negate the computed int.MaxValue and call it a day

int.MinValue == -int.MinValue

But if we do so we will get the value of -2147483647 which is one too big from the actual minimal value of -2147483648. Ah, the rouge one again, and we know exactly where that one comes from!

Remember that signed integer values are stored as two’s complement, which is computed by flipping all the bits of the number and adding 1? Therefore, to get the correct minimal value we need to subtract one from negated computed integer maximal value.

int.MaxValue : int.MinValue == -int.MinValue - 1

One interesting thing : is if we just apply ~ operator to int.MaxValue, we get the correct int.MinValue.

uint minUint = 0;
uint maxUint = ~0U;
int maxInt = (int)(maxUint >> 1); // typecast to make compiler happy
int minInt = -maxInt -1;
int minInt2 = ~maxInt;

Now we have all four constants, and no one can rob us of them anymore!

Leave a Reply

Your email address will not be published. Required fields are marked *