我对C#中数字类型的丢失信息感到有些困惑。
当我这样做时:
int x = 32780;
short y = (short)x;
我有结果:-32756为y而不是预期的32767.为什么?如何计算?
短期范围:-32768至32767 范围:-2,147,483,648至2,147,483,647
答案 0 :(得分:53)
你似乎期待一种“四舍五入”的效果,而不是实际发生的事情,这是对数据的逐位重新解释。
在二进制中,x
等于00000000000000001000000000001100
,这是一个32位数,只有16位有效位。 short
是一个16位签名的整数,使用two’s complement notation表示。
转换后,x
的最后16位将被复制到y
,并提供1000000000001100
。重要的是,第一个数字是1
。在二进制补码表示法中,这是-32756
。你的号码没有被舍入 - 它被读作好像是16位。
答案 1 :(得分:14)
请阅读丹的答案,了解发生这种情况的“真实”原因,但这样就足够了:当一个数字溢出其最大值时,它会回绕到最小值。所以,32780 - 32767 = 13
和-32768 (which is one of the 13) + 12 (the other 12) = -32756
答案 2 :(得分:6)
可以说运行时添加或减去65536
所需的次数,以获得short
(System.Int16
)类型范围内的数字。
如果你知道很多纯数学,而不是通过内部二进制表示(也很酷)来解释它,你可以从modular arithmetic理解它。
为此,请将类型short
视为模数为65536的整数,也写为ℤ/65536ℤ。 ℤ/65536ℤ的每个成员都是同余类,即除以65536时所有具有相同余数的数字。现在,每个同余类具有不同成员(“代表”)的无限。如果您选择一个,则通过重复添加或减去65536来获得所有其他。
使用short
数据类型,我们会在-32768
到+32767
的时间间隔内选择唯一代表。然后ushort
是同一件事,我们只会在0
到65535
中选择代表。
现在关于ℤ/65536ℤ的一个很酷的事情就是它形成了ring,其中我们有加法,减法和乘法(但不除法)。实际上,在unchecked
上下文中,使用short x,y;
,C#操作
(short)(x + y)
(short)(x - y)
(short)(x * y)
完全对应于ℤ/65536ℤ中的算术。 (我们必须在此转换回short
,因为从技术上讲,C#仅为int
,uint
,long
和ulong
定义运算符。)
以同样的方式,sbyte
和byte
可以被认为是戒指ℤ/256ℤ,int
和uint
为ℤ/4294967296ℤ,{{1 }和long
为ℤ/18446744073709551615ℤ。
ulong
满足
int X
因此不清楚unchecked( 10 * X == 35 ) // integers Int32
应该是什么。另一方面,两个 35/10
满足
X
但是哪一个应该是unchecked( 10 * X == 36 ) // integers Int32
?
然而,只有一个36/10
使
int X
真。我们有运气,因为11对于4294967296是相对素数。解unchecked( 11 * X == 35 ) // integers Int32
是1952257865(自己检查),所以商X
在某种意义上是35/11
。
结论:C#的整数运算X
,+
和-
可以简单地解释为ℤ/nℤ中的环运算。但是*
操作与戒指没有任何关系!
答案 3 :(得分:3)
当您将x转换为short时,您将获得溢出(即分配的值超出其可处理的值),并且您的值将表示为从min值开始再次重新开始。正如你所说,short的最大值是32767所以你的新值将是:-32768 +(32780 - 32768)= -32756
根据编程语言(可能还有编译器),您尝试做的事情可能会带来异常。它似乎在C#中没有。
答案 4 :(得分:2)
有溢出。如果您希望将数字限制为最大值,可以通过将整数与short.MaxValue
进行比较来解决此问题:
short y;
if (x > short.MaxValue) {
y = short.MaxValue;
} else if (x < short.MinValue) {
y = short.MinValue;
} else {
y = (short)x;
}