Reputation: 487
I'm a little confused about loss information on numeric types in C#.
When i do this:
int x = 32780;
short y = (short)x;
I have the result: -32756 for y and not the expected 32767. Why? How this is calculated?
Range of short: -32768 to 32767 Range of int: -2,147,483,648 to 2,147,483,647
Upvotes: 19
Views: 2560
Reputation: 34218
You seem to be expecting a “rounding down” effect rather than what is actually happening, which is a bitwise re-interpretation of the data.
In binary, x
is equal to 00000000000000001000000000001100
, which is a 32-bit number with only 16 significant bits. A short
is a 16-bit signed integer which is represented using two’s complement notation.
When you convert, the last 16 bits of your x
are being copied into y
, giving 1000000000001100
. Importantly here, the first digit is a 1
. In two’s-complement notation, this is -32756
. Your number hasn't been rounded—it’s been read as though it were 16-bit.
Upvotes: 53
Reputation: 62012
One could say that the runtime adds or subtracts 65536
as many times as is needed to get a number within the range of the short
(System.Int16
) type.
If you know a lot of pure math, instead of explaining it through the internal binary representation (which is also cool), you can understand it from modular arithmetic.
For this, consider the type short
to be the integers modulo 65536, also written as ℤ/65536ℤ. Each member of ℤ/65536ℤ is a congruence class, i.e. a set of numbers all having the same remainder when divided by 65536. Now, each congruence class has an infinitude of different members ("representatives"). If you pick one, you get all the others by repeatingly adding or subtracting 65536.
With the short
data type, we pick the unique representative in the interval -32768
through +32767
. Then the ushort
is the same thing, only do we pick the representative in 0
through 65535
.
Now the cool thing about ℤ/65536ℤ is that it forms a ring in which we have addition, subtraction and multiplication (but not division). And actually, in unchecked
context, with short x,y;
, the C# operations
(short)(x + y)
(short)(x - y)
(short)(x * y)
correspond exactly to the arithmetic in ℤ/65536ℤ. (We have to cast back to short
here because technically C# defines the operators only for int
, uint
, long
, and ulong
.)
In the same way, sbyte
and byte
can be thought of as the ring ℤ/256ℤ, int
and uint
as ℤ/4294967296ℤ, and long
and ulong
as ℤ/18446744073709551615ℤ.
Note, however, that because these moduli are not primes, division is not possible in the ring. For example, no int X
satisfies
unchecked( 10 * X == 35 ) // integers Int32
and therefore it's not clear what 35/10
should be. On the other hand, two X
satisfy
unchecked( 10 * X == 36 ) // integers Int32
but which of them should be 36/10
?
However, exactly one int X
makes
unchecked( 11 * X == 35 ) // integers Int32
true. We're having luck because 11 is relatively prime to 4294967296. The solution X
is 1952257865 (check for yourself), so the quotient 35/11
is in a sense that number X
.
Conclusion: The integer operations +
, -
, and *
of C# can be interpreted as simply the ring operations in ℤ/nℤ. But the /
operation is in no way related to the ring!
Upvotes: 6
Reputation: 2053
When you cast x to short you will get an overflow (i.e. the value being assigned more than it could handle) and your value would be represented as thought it would start over again starting from the min value. As you said, short's max value is 32767 so your new value would be: -32768 + (32780 - 32768) = -32756
Depending on the programming language (and perhaps your compiler), what you're trying to do could bring an exception or not. It seems in C# it doesn't.
Upvotes: 3
Reputation: 51694
There is an overflow. If you want the number to be capped to the maximum value, you can work around this by comparing the integer to short.MaxValue
:
short y;
if (x > short.MaxValue) {
y = short.MaxValue;
} else if (x < short.MinValue) {
y = short.MinValue;
} else {
y = (short)x;
}
Upvotes: 2
Reputation: 8818
Read Dan's answer for the "true" reason this happens, but it'll suffice to think about it like this: When a number overflows its maximum, it loops back around to the minimum. So, 32780 - 32767 = 13
and -32768 (which is one of the 13) + 12 (the other 12) = -32756
Upvotes: 14