Different behaviour between int datatype in C++ and C#!

Ah hello.

I am getting confused by what is probably very simple.

I have an SDK whose functions return #defined error codes.  All of these functions return an int.  Now, one of these error codes is defined as 0xB0000007, which is

2952790023, or

1011 0000 0000 0000 0000 0000 0000 0111

in binary.

I first started using this SDK in C++.  So, I would call a function, then check its return value.

int n = <function call>;
if ( n == 0xB0000007 )
{
      // Error
      HandleError();
}

I did not think anything of it at the time until I started using C#, but 2952790023 is outside the range of an int.  So, when I hover over the value of n, the intellisense tells me it is -1342177273.  But, strangely, HandleError() still gets called.

For the C++ team:
================

Why is this happening, -1342177273 is not 2952790023 !

For the C# team:
===============

With the same code:

int n = <function call>;
if ( n == 0xB0000007 )            // ***
{
      // Error
      HandleError();
}

The C# compiler tells me

"Comparison to integral constant is useless; the constant is outside the range of type 'int'"

regarding line *** (which incidentally is what got me thinking about why the C++ code works!).  Sure enough, when I call the function in such a way that it will return 0xB0000007, HandleError() does *not* get called, even though the value of 'n' is -1342177273, which is the same as in C++!

Why does C# not behave the same way as C++, and how can I reliably test the return value of the function call against 0xB0000007?

TIA
LVL 19
mrwad99Asked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

phoffricCommented:
In my calculator,

0xFFFFFFFFB0000007 =  -1342177273
                0xB0000007 = 2952790023  

coincidence - probably not
0
Infinity08Commented:
>> Why is this happening, -1342177273 is not 2952790023 !

An int is a signed integer type (the unsigned equivalent is called unsigned int).
Those two values have the same bit pattern (0xB0000007) on a 32bit architecture, so they compare equal.
0
phoffricCommented:
You have 32 bits to work with and you are likely having signed, unsigned int issues.
0
Fundamentals of JavaScript

Learn the fundamentals of the popular programming language JavaScript so that you can explore the realm of web development.

Infinity08Commented:
This means that if you use this :

        unsigned int n = <function call>;

and hover above n, you should see the value 2952790023.

If you then put :

        int n2 = n;

and hover above n2, you should see the value -1342177273.
0
Infinity08Commented:
What I said was for the C++ code btw.
0
phoffricCommented:
Notice that 0xFFFFFFFFB0000007 in my calculator is more than 32 bits; but if all you have is 32 bits, then the leading 1's (i.e., 0xF is 1111b) are not available; that leaves you with 0xB0000007, which is 32 bits.
0
evilrixSenior Software Engineer (Avast)Commented:
>> Why is this happening, -1342177273 is not 2952790023 !
As int is a signed type it is just overflowing into a negative value. If you cast that to an unsigned int it would be 2952790023. They both have the same bit pattern.

>> "Comparison to integral constant is useless; the constant is outside the range of type 'int'"
C# is a lot stricter with its types.

>> and how can I reliably test the return value of the function call against 0xB0000007?
Try handling it as an uint in C#
0
evilrixSenior Software Engineer (Avast)Commented:
eeeks! Sorry chaps :)
0
phoffricCommented:
No problem chap :)
0
pgnatyukCommented:
MSDN. C# for C++ Developers
http://msdn.microsoft.com/en-us/library/yyaad03b(VS.90).aspx

"The long type: In C#, the long type is 64 bits, while in C++, it is 32 bits."

But in your case, int is int.
2952790023 is 0x0xB0000007
0
phoffricCommented:
Other comparisons between signed and unsigned int can really create havoc. For example, if you have:

if( asigned < bunsigned )  {...}

then if you happen to have asigned set to -1, and bunsigned set to 5, then the signed int asigned gets promoted to unsigned int (but the bit pattern remains the same - it's all in the interpretation). So in this case even though -1 < 5 is true (in your mind), the execution sees 0xFFFFFFFF < 0x00000005 which is false.
0
pgnatyukCommented:
>>As int is a signed type it is just overflowing into a negative value
Oh. you are always right.
0
mrwad99Author Commented:
OK, thanks all.

Can I pick up on the following points please:

>> As int is a signed type it is just overflowing into a negative value. If you cast that to an unsigned int it would be 2952790023. They both have the same bit pattern.

-1342177273 is

1111 1111 1111 1111 1111 1111 1111 1111 1011 0000 0000 0000 0000 0000 0000 0111

2952790023 is

1011 0000 0000 0000 0000 0000 0000 0111

??

>> Try handling it as an uint in C#

Yes, that works.  Thanks :o)

>> As int is a signed type it is just overflowing into a negative value.

Can someone explain exactly how that is happening, please?

>> "The long type: In C#, the long type is 64 bits, while in C++, it is 32 bits."

I tried casting my int to a long and got back -1342177273, which would suggest overflow.  But how can that happen when we have 64 bits - enough space to hold 0xB0000007??

Finally, when I cast the result of my function to an UInt64, I get the result as

0xffffffffb0000007

Why has 0xffffffff been prepended?
0
phoffricCommented:
>> I tried casting my int to a long
If that is C++, then it is likely that your int and long are the same signed 32 bit type.
0
mrwad99Author Commented:
Nope, that was in C#...
0
phoffricCommented:
In C++, a 32 bit signed long has a max positive value of 0x7FFFFFFF
Add 1 to this number and you get 0x80000000, which is the smallest negative number (notice the msb is 1, which for signed 32 bit number indicates a negative number). In this case adding 1 to the largest positive number gave you the smallest negative number.

In C++ code below, small is 0xb0000007
The 64 bit unsigned big variable when set to small results in a signed conversion of the negative value first to 64 bits extending the sign bit to keep the same negative value. Then this bit pattern is copied to big yielding 0xffffffffb0000007

But with big2, I first converted the small to a 32 bit unsigned number (same bit pattern) but now the compiler says that the RHS of the = sign is a positive number. So when initialiazing big2, you get a positive number, 0x00000000b0000007


   long small = -1342177273;
   unsigned long long big = small;
   unsigned long long big2 = (unsigned long) small;

Open in new window

0
evilrixSenior Software Engineer (Avast)Commented:
>> Can I pick up on the following points please
On a signed int only the top bit (MSB) is needed to show sign. If both types are 32 bit then both have identical bit patterns.

>> Yes, that works.  Thanks :o)
Yup, cos the MSB is not being used to show sign, it is being used as part of the +ve (positive) number. See below

>> Can someone explain exactly how that is happening, please?
When all 31 bits are full the 32nd bit then gets set... on an unsigned in this just allows more +ve number but on a signed int it means the number is negative so it starts counting backwards every time you add 1 more cardinal value to it.

>> I tried casting my int to a long
On Windows, in C++, a long and int are both 32 bit. On C# a standard int is 32 bit and a long is 64 bit.

>>Why has 0xffffffff been prepended?
Cos you've added a 32 bit negative to a 64 bit negative so all the other bits are set to 1 to show the whole number is negative.  This is just standard twos compliment (the most common way to show negative numbers in binary).
http://en.wikipedia.org/wiki/Two's_complement

                                        Most
                                        Significant
                                        Bit (32 bit)
                                        |
                                        v
1111 1111 1111 1111 1111 1111 1111 1111 1011 0000 0000 0000 0000 0000 0000 0111 64 bit signed representation of 2952790023 == -1342177273
                                        1011 0000 0000 0000 0000 0000 0000 0111 32 bit signed representation of 2952790023 == -1342177273

0000 0000 0000 0000 0000 0000 0000 0000 1011 0000 0000 0000 0000 0000 0000 0111 64 bit unsigned representation of 2952790023 == 2952790023 
                                        1011 0000 0000 0000 0000 0000 0000 0111 32 bit unsigned representation of 2952790023 == 2952790023 

Open in new window

0
phoffricCommented:
Oh, my previous post was also C++.
0
Infinity08Commented:
>> But how can that happen when we have 64 bits - enough space to hold 0xB0000007??

In C++, 0xB0000007 is an int literal value.
If you want an unsigned int literal value, you need to use 0xB0000007U (note the U at the end).

I bet something similar is true in C#.


>> Why has 0xffffffff been prepended?

Sign extension. Casting a 32bit signed integer value to a 64bit unsigned integer value would do that.
If you're working with unsigned values, then use unsigned types.
0
mrwad99Author Commented:
OK.

*** You will all have to excuse me here because I am not too good when it comes to things like this, but I really do have a penchant for understanding, as I am sure you will know :) ***

RX, I am taking apart your statement:

"When all 31 bits are full the 32nd bit then gets set... on an unsigned in this just allows more +ve number but on a signed int it means the number is negative so it starts counting backwards every time you add 1 more cardinal value to it."

OK.  So I have the max value for a 32 bit integer, 2,147,483,647, or

0111 1111 1111 1111 1111 1111 1111 1111

On Windows calculator, if I add one to that, I get

1000 0000 0000 0000 0000 0000 0000 0000.

But then I toggle back to Decimal view and it tells me it is 2,147,483,648!  There does not appear to be a way to limit the bitness of decimal values in calculator, so,

1) So this is -2,147,483,648, right?  Which means that

1111 1111 1111 1111 1111 1111 1111 1111

is -1, right?

Now, if I am to assign 2,952,790,023 to a 32 bit int, according to what you have said above, and from what has been shown in the Wikipedia 2's comp link, I will get a maximum of 2,147,483,647, the number will be incremented then the sign will be changed, and the remainder of what is to be added will in fact be added to the now negative maximum:

We have 805,306,376 left over, which, added to -2,147,483,648, gives -1,342,177,272.

This is one more than what I calculated in my original question; -1,342,177,273.

2) How can we explain this?

Now, regarding why 0xffffffff has been prepended:

If all we want to do is keep the sign the same (-ve) then why don't we just set the MSB to 1; i.e.

1000 0000 0000 0000 0000 0000 0000 0000

Why do we have to set all 32 to 1??  According to the Wikipedia link, it is just that bit that determines signage...
0
Infinity08Commented:
>> We have 805,306,376 left over, which, added to -2,147,483,648, gives -1,342,177,272.
>> 
>> This is one more than what I calculated in my original question; -1,342,177,273.

You were adding to 2,147,483,647, not to -2,147,483,648. That causes the difference of 1.

ie. 2,147,483,647 + 1 becomes -2,147,483,648.
and then -2,147,483,648 + (805,306,376 - 1) = -1,342,177,273

The -1 in (805,306,376 - 1) is because you already added 1 in (2,147,483,647 + 1)


>> If all we want to do is keep the sign the same (-ve) then why don't we just set the MSB to 1; i.e.

We don't just want to keep the sign. We also want to keep the value.

0xFFFFFFFF interpreted as a 32bit signed int, corresponds to the value -1.
0xFFFFFFFFFFFFFFFF interpreted as a 64bit signed int, also corresponds to the value -1.
0x80000000FFFFFFFF interpreted as a 64bit signed int, however, corresponds to the value -9223372032559808513.

The same is true for any other negative value. For example -50 :
as a 64bit signed int : 0xFFFFFFFFFFFFFFCE
as a 32bit signed int : 0xFFFFFFCE
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
itsmeandnobodyelseCommented:
To add to above comments:

>> As int is a signed type it is just overflowing into a negative value.

>>>> Can someone explain exactly how that is happening, please?

A 32-bit signed integer is in the range -2^31 to 2^31-1

    [-2147483648        ....             -1  0 1             ...         2147483647]

A 32-bit unsigned integer is in the range 0 ... 2^32-1
                                         
                                                        [0 1             ...         2147483647  2147483648    ...    4294967295]

If you now duplicate the first range you see where both the ranges do overlap

    [-2147483648  ....    -1  0 1 ...   2147483647][-2147483648  ....                  -1  0  1 ...   2147483647]

        ...        4294967295][0 1 ...   2147483647    2147483648  .... 4294967295]


We have (all assertments were for 32bit) :

   signed int          unsigned int     hex               binary
---------------------------------------------------------------------------------------------------------------------

                  -1  == 4294967295   (0xFFFFFFFF)   1111 1111 1111 1111 1111 1111 1111 1111
                   0  == 0                     (0x0000000)   0000 0000 0000 0000 0000 0000 0000 0000
 2147483647  == 2147483647   (0x7FFFFFFF)   0111 1111 1111 1111 1111 1111 1111 1111  
-2147483648  == 2147483648   (0x8000000)   1000 0000 0000 0000 0000 0000 0000 0000
-2147483647  == 2147483649   (0x8000001)   1000 0000 0000 0000 0000 0000 0000 0001
                  -1  == 4294967295   (0xFFFFFFFF)   1111 1111 1111 1111 1111 1111 1111 1111

So the interesting part is when you add 1 to 2147483647. The bit 31 (highest bit) now turns to 1 and all other bits to zero.

For a signed int that means an overflow cause the negative numbers exactly were defined as those where the bit 31 is 1.

For an unsigned int the overflow happens if you add 1 to 4294967295. Then the result is 0 as you need to set all 32 bits to zero and have a 33th which you set to 1. But as there are only 32 bits the overflow (in almost all systems) simply would be ignored and 0xffffffff turns to 0x00000000. Looking at the same number as a signed int, the whole thing is perfect cause -1 + 1 == 0 .

>> I tried casting my int to a long and got back -1342177273, which would suggest overflow.  But how can that happen when we have 64 bits - enough space to hold 0xB0000007??

when the left side is a 64bit signed integer and you assign a 32bit number from the right side, it doesn't depend on the left side what happens but on the right side.  If the right side is of type unsigned it would assign a positive number. If the right side is a signed it would assign a negative number. The 0xB0000007 is a literal and the hex notation got lost (or better was ignored) and does *not* determine the type as unsigned. Hence it was treated as a signed integer. negtive signed integers for 64bit are those bit-combinations where the highest bit 63 is 1. That's why a 64bit negative gets all high bits padded up with 1 (similar as we had with -1 for 32bit integers). Note, for a 64bit we have no overflow in that case but simply the rule for negative numbers applies where the high bit was set to 1 and *all* other bits were toggled.

0
mrwad99Author Commented:
OK, I will think about that.  

So overall it has been shown why overflow occurs, and how we get the -ve value.  This has been demonstrated using two's compliment.  RX's comment above states that, essentially, 2's comp is just one way of doing this kind of thing, so is it fair to say that it can always be used, or are there some cases where we would have to use another method?
0
Infinity08Commented:
You should not rely on how your platform handles negative values. Two's complement is common, but not the only way of doing it.
0
evilrixSenior Software Engineer (Avast)Commented:
>>  is it fair to say that it can always be used,
It can but it doesn't have to be. C++ generally uses two's compliment but the standard leave it open to others.
http://en.wikipedia.org/wiki/Signed_number_representations

>> or are there some cases where we would have to use another method?
Twos compliment is most often used because it's simple and (for the most part) intuitive.
0
mrwad99Author Commented:
itsmeandnobodyelse, I am reading your comment.  Sorry, I missed the mail notification saying you had posted before I posted my last comment.

I8/RX:
But is the result always going to be the same, regardless of which method is used, i.e. can we say that assigning 2952790023 to a 32 bit int will always give -1342177273??
0
Infinity08Commented:
>> i.e. can we say that assigning 2952790023 to a 32 bit int will always give -1342177273??

When two's complement is used, yes. Otherwise, not necessarily.
0
Infinity08Commented:
What you can rely on though, is that casting from a signed to an unsigned int and back, will give you the same (original) value.
0
evilrixSenior Software Engineer (Avast)Commented:
>> But is the result always going to be the same, regardless of which method is used
Take a look at the Wikipedia link I gave you above to see how each represents negative numbers.
0
mrwad99Author Commented:
Phew.  OK, I am going to close this now.

Once again, thanks to all for the information given, it is greatly appreciated :)  I have increased the points to 500 for a point allocation in accordance with the effort given...
0
evilrixSenior Software Engineer (Avast)Commented:
mrwad99,

If you need any further clarification please don't hesitate to post back here.

-Rx.
0
mrwad99Author Commented:
Thanks RX, appreciated.  I may need to take you up on that...

If I have missed anyone on the points, please post here...
0
itsmeandnobodyelseCommented:
>> If all we want to do is keep the sign the same (-ve) then why don't we just set the MSB to 1;

That indeed would have been an alternative method to the two-complement.

There are a few disadvantages, though (and no real advantages):

   - we would have two bit combinations for 0 (0x0000000 and 0x80000000)
   - arithmetic operations would need to convert negative operands before use
   - we had two integer sets from 0 to -2^31-1 and 0 to 2^31-1 which were discontiguous
      regarding theit bit combinations and each set would overflow to itself at both ends.
   - int and unsigned int wouldn't match as in your initial sample what would generate
     (more) problems when having overflow at the 0 end of unsigned ints.

   

0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.