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!