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