Do you know your limits?Filed Under: Weekly Tuesday Dose of goodness
Hi all,
This week’s post came late due to my hectic schedule and I suspect that I might have to even go for a miss for next week’s post.
In any case, I’ll try my best to post an article every tuesday.
This week’s post is very simple - limits.
A lot of people don’t know theirs. Do you know yours? Read on…
Introduction
The limits we’re referring to are actually very simple things such as native types. For example char, short, int, long, float, double, etc…
So, most of us roughly know what are the limits of these types. But do we really know?
Most of the time we’ll see the compiler throwing out warnings, telling us not to mix signed and unsigned types. I’m very sure that this is one of the warnings that is almost certainly ignored totally.
In the following paragraphs, I’ll talk about these limits and how they affect our daily applications and development decisions.
Signed and Unsigned
So what’s the difference? Irritating isn’t it? Well … not really if you know what to do with it.
First of all, unsigned types do not apply to floating point types. They’re already tied up with storing the mantissa and decimals.
Secondly, unsigned types cannot be negative.
Thirdly, unsigned types have twice the limit range in terms of maximum positive number than their signed variants.
Lastly, in most cases, bit shifting is safe across most platforms for unsigned types compared to signed types.
As for signed types, there’re a few characteristics that one should know.
Firstly, they have a negative range. Based on 1’s compliment, their negative range is always 1 numeric more than their positive range.
That is, for example, signed char, or simply char, positive range is 0 - 127, the negative range is -1 to -128.
Secondly, their first most significant bit is used as the sign indicator.1 for positive, 0 for negative. See here for more information.
Lastly, this - I’m not so sure but I’ve heard that bit shifting doesn’t work properly on some platforms. We’ve tried it on Visual Studio 2005 without any issues. Basically, the sign bit is first taken out, then the rest of the bits are shifted without the sign bit. Then the sign bit is set back to the most significant bit after it’s done.
Limitations
Every primitive type has a limitation in terms of how far they go. Going beyond these limitations doesn’t lead to a crash. At least not for C++.
Instead, when the maximum limit of a primitive type is reached and that the limit is crossed, the type simply rolls over and start all over again.
For example, let’s take a look at the unsigned char type again. It has 8 bits and thus looks like:
0000 0000
When it’s full, it looks like:
1111 1111
And that’s 255 in value. (Consider 0 as a number as well).
What happens when you add 1 into the unsigned char? Let’s find out:
1111 1111 +0000 0001 --------- 0000 0000
Do note - this is not an OR operation. OR operations always appear to behave like “add” operations. But it’s not true.
In this case, the bits will simply flow over and. The expected answer was supposed to be:
1 0000 0000
But, since unsigned char only has 8 bits to hold all the data, that 1 last most significant bit is truncated and thus resetting the value to zero!
Live Example
Let’s take a look at live example. The following example that I’m about to share is based on an signed short.
We know that short is usually 2 bytes long and since it’s signed, the ranges are:
0 to 32767
-1 to -32768
So what happens when I set -1 as the amount of credits for the game below?

-1 as credits works in Dune 2
But, do you know what’s binary state of -1 in a signed short type?
It's 1111 1111 1111 1111 !!!
So what happens when I reduce credits for building structures?

When you use credits, the number goes up instead even when it's negative due to the bit arrangement.
Let’s take a look at the binary state of -321:
1111 1110 1011 1111
So, there’re plenty of bits to spend before reaching:
0111 1111 1111 1111 = 32767
Thus, you’ll have as much as 65536 credits to build your army. The hack is really simple. You’ll only need to modify the file scenario.pak in the game directory without modifying the format of the file (best done via a hex editor) and you’ll get what you want!
I won’t go into details though
The reverse, however, happens as well. This is when the harvester collects enough spice, which I think it’s around 600 credits per bulk and returns to the refinery.
So … if you take -321 credits and add it with 600 credits, what happens?
You’ll get 279 credits! In binary this is how it looks like:
0000 0001 0001 0111
In terms of binary operations, it’s correct because the bits have overflowed at the last possible bit, which is,
1111 1111 1111 1111 = -1
Thus, as much as the hack has promised you, it’ll also take it all away from you in an instant. It’s just like karma man… especially when you’re immersed in attacking the enemy base and forgot to tend to your harvesters who diligently harvest all the spice in the area, only to find that by doing so, it inadvertently causes you to lose a potential 60,000+ credits!
Example in a bank
I’ve once encountered a strange bug when I was working for a bank which I won’t name. Basically, this account has like 10,000,000,000 in his account. This is like 10 billion; it’s a test account by the way.
It’s stored nicely in the database.
So, the code retrieve this value and stored into an unsigned long type. But the value that’s represented in the unsigned long is wrong.
Note that the unsigned long in this case is 32-bits long.
So why is the value wrong?
Reason is simple - the 32-bit long type can only store a number up to 2^32, which yields roughly around 4.2 billion.
In this case, the binaries would have overflowed several times before ending up with whatever value it’s in.
You can try it in your IDE and see if you can get the weird result.
How to tell the maximum size?
Very simple. There’re 2 methods in which you can derive the maximum size.
1) Perform a sizeof(type) ie, sizeof(int) and get the byte size, then perform a 2 power of byte size to retrieve the maximum range.
2) Read up on the specification of all the primitive types via documentation and perform the same 2 power of byte size to get the maximum range.
However, do note. For signed variants, always use a 1’s compliment to derive the negative side as well as to divide your results by 2 first.
For example, signed short range is 0 to 32767, -1 to -32768. It’s unsigned variant is 0 - 65535.
Conclusion
In conclusion, knowing your limits of your primitives will serve you a long long way in your development or programming career.
It’ll also arm yourself with things to look out for, especially suspicious numbers that are exceptionally large.
Without all these knowledge, a programmer is blind when choosing primitives and will be confusion by the range of selection available to him/her.
I’m sure some of you like myself, would have asked, why need short when we have int and why need int when we have long?
A short reason for it is - in the past, data alignment is packed to the brim. That is, unlike nowadays where struct alignment is defaulted at 8 bytes for maximum memory access efficiency, it was as low as 2 bytes during the 16-bit computing era. This help conserves memory usage per object instance and will allow an otherwise un-executable application to be run on machines with very little memory.
We’re talking about memory as small as 640kb where RAM takes on an appearance of a multi-legged bug.
Yeah, those were the old days of floppy disks, small hard drives and very small RAM capacities in the 8086 era.
Nevertheless, the significance of primitive limits now extends to design specifications where a certain variable is limited in range for design reasons.
Knowing it can help save you from getting into weird logic bugs which are hard to detect unless you’re a binary genius.
With that, I’m signing off for this week.
Have a great week ahead!
Signing off,
Jeremy
- Permalink
- Admin
- 27 Jul 2010 2:28 PM
- Comments (0)