C++ unsigned long doesn't wrap around after 4294967295

1.2k views Asked by At

I was playing with some c++ code, and found that if I start a unsigned long with 4294967295 (its max allowed value) and add let's say 6 I MUST get 5, and it does! but the following operation of addition, and mod 255 does not give the correct answer. why this happens, and how it can be avoided?

Plataforms Tested:
Windows 7, Visual Studio c++ : Works Fine;
CentOS release 6.6 (Final), g++ : Give The Wrong Answer;
See NOTE below the code:

#include <stdio.h>
int main(int argc, char *argv[])  {
   unsigned long s1=4294967295;
   unsigned long vp=245;
   unsigned long a;
   unsigned long b;
   unsigned long result;

   /* s1 is 4294967295 + 6 wich is 5  */
   s1+=6;
   a=s1;
   b=255*vp*s1;
   result = (a + b) % 255;
   printf("s1=%u , a=%u , b=%u , (a+b)=%u , (a+b)%255= %u\n", s1, a, b, a + b, result);

   /* s1 is a static value 5 */
   s1=5;

   a=s1;
   b=255*vp*s1;
   result = (a + b) % 255;
   printf("s1=%u , a=%u , b=%u , (a+b)=%u , (a+b)%255= %u\n", s1, a, b, a + b, result);
   return 0;
}

NOTE: if I compile the code with Visual Studio, I get the correct answer.

s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 5
s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 5

BUT, if I compile with gcc version 4.4.7 20120313 (Red Hat 4.4.7-11) (GCC) I get:

s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 6
s1=5 , a=5 , b=312375 , (a+b)=312380 , (a+b)%255= 5

How it can be avoided in g++?

2

There are 2 answers

2
Gilles 'SO- stop being evil' On BEST ANSWER

if i start a unsigned long with 4294967295 ( his max allowed value )

4294967295 is only the maximum value for unsigned long if unsigned long is a 32-bit type. This is the case on Windows, both 32-bit and 64-bit. On Linux, unsigned long is a 32-bit type on 32-bit platforms but a 64-bit type on 64-bit platforms. The C++ standard doesn't mandate a particular size for types, it only says that unsigned long must be at least 32 bits, so both Windows and Linux are correct.

If you want a guaranteed 32-bit type, use uint32_t from <cstdint> if available. It's a recent addition to the language, I don't know if your version of Visual Studio has it yet; if it doesn't, see 'uint32_t' identifier not found error.

Also, beware that there are bugs in the code you're using to print out the values. It happens to not crash on the two platforms where you tried, but it truncates the result in GCC. To print out an unsigned long, you need to use the %lu specifier, not %u. To print out a percent character, you need to double the %.

printf("s1=%lu , a=%lu , b=%lu , (a+b)=%lu , (a+b)%%255= %lu\n", s1, a, b, a + b, result);

Or alternatively, since you're using C++, use its own printing mechanism.

#include <iostream>
…
    std::cout << "s1=" << s1
              << ", a=" << a
              << ", b=" << b
              << ", (a+b)=" << (a+b)
              << ", (a+b)%255=" << result;

On a platform where unsigned long is a 64-bit type, the value of s1 the first time round is 4294967301. The value of b is 268328082129975. You'll see these values if you use the correct printing specifiers.

2
Some programmer dude On

Are you on a 64-bit system? Because then long is usually 64 bits in GCC, while it's still 32 bits in the Visual Studio compiler, which will explain why it doesn't work with GCC but works with VS.

To get the maximum value of some type, use `std::numeric_limits to get it, e.g.

unsigned long s1 = std::numeric_limits<unsigned long>::max();