有人可以用二进制解释 C# 中的溢出吗?
Can someone explain overflow in C# using binary?
我目前正在阅读一本关于 C# 编程的书,它简要介绍了上溢和下溢,作者给出了当你超过某种类型的允许范围时会发生什么的一般概念。
示例
short a = 30000;
short b = 30000;
short sum = (short)(a + b); // Explicitly cast back into short
Console.WriteLine(sum); // This would output the value -5536
所以short类型的范围只有-32768到32767,书中作者给出的解释是
"For integer types (byte, short, int, and long), the most significant bits (which overflowed) get dropped. This is especially strange, as the computer then interprets it as wrapping around. This is why we end up with a negative value in our example. You can easily see this happening if you start with the maximum value for a particular type (for example, short.MaxValue
) and adding one to it." C# 玩家指南第二版,第 9 章,第 58 页
你最终会达到最低 (-32768)。
我无法理解这一点当作者谈到 "the computer interprets it as wrapping around"
时我感到困惑
我尝试理解的方式是
short 类型使用 2 个字节(16 位)
所以数字 32767 = 0111111111111111
如果我要 +1 到二进制字符串,我最终会得到
32768 = 1000000000000000(不能用 short 类型表示,因为最大值是 32767)所以编译器给出 -32768。为什么最后会变成负数?
我理解使用二进制补码表示负数的概念,有人可以在这里纠正我的想法或详细说明,因为我不完全理解为什么我们只使用 16 位中的 15 位来表示正数范围和负范围的最高有效位
忽略所有告诉你最高位是符号位的人。这是错误的思考方式。
正确的思考方式是:
- 我们有 65536 种可能的位模式。
- 因此我们可以表示 65536 个可能的数字。
- 我们必须有一个 map 为每个位模式分配 意义。
对于无符号短裤,我们按如下方式分配位模式:
0000000000000000 --> 0
0000000000000001 --> 1
...
0111111111111111 --> 32767
1000000000000000 --> 32768
1000000000000001 --> 32769
...
1111111111111111 --> 65535
对于签名短裤,我们使用以下约定:
0000000000000000 --> 0
0000000000000001 --> 1
...
0111111111111111 --> 32767
1000000000000000 --> -32768
1000000000000001 --> -32767
...
1111111111111111 --> -1
就这么简单。
为什么要使用这个约定?
三个原因:
(1) 无论你是有符号的还是无符号的,前 32K 个值都是一样的。太方便了。
(2) 在这两个约定中,"all zero bits" 表示零。
(3) 因为加法在两种约定中的作用完全相同!
我们有
0000000000000000 --> 0
我们想加一个。我们使用二进制规则添加一个,我们得到:
0000000000000001 --> 1
无论短片是有符号还是无符号,这都有效。
我们有无符号空头:
1000000000000000 --> 32768
我们想加一个。我们使用二进制规则这样做,我们得到正确的答案:
1000000000000001 --> 32769
签名短裤也一样。我们有
1000000000000000 --> -32768
我们希望添加一个。我们使用二进制规则这样做,我们得到:
1000000000000001 --> -32767
同样,您可以通过将 1111111111111111
加到任何二进制数来验证您得到 "one less",因此减一和加一一样有效。然后您可以继续证明,一般来说,加法和减法在有符号和无符号算术中的工作方式相同,这意味着处理器不必知道编译器认为这些值是有符号还是无符号.
这就是我们使用二进制补码的原因:因为无论您是在进行有符号运算还是无符号运算,基础数学都是完全相同的。
请注意,我对其中的 "sign bit" 只字未提。高位设置为负数这一事实只是一个不错的奖励。我们想要的真正 属性 是只需要构建硬件来进行数学计算 一次。
二进制补码只是一种约定,用于根据与保存该位模式的变量关联的类型 将两种可能含义之一分配给位模式。作为一个行业,我们选择这个约定是因为制造使用这个约定的高性能硬件很便宜。
我们可以选择许多其他约定。例如,我们可以说对于有符号数,我们使用映射:
0000000000000000 --> -32768
0000000000000001 --> -32767
...
0111111111111111 --> -1
1000000000000000 --> 0
1000000000000001 --> 1
...
1111111111111111 --> 32767
请注意,这与之前完全相同,除了最高位!
在这种解释中,加法仍然像我们预期的那样起作用。但是这个map没有short和ushort前32K值相同的属性,也没有全零位表示0的属性。所以我们不使用这个约定,因为它不太方便。在这个约定中,从 short 转换为 ushort 需要做一个加法。将短路设置为零需要将字节设置为非零的值。我们可以使用带符号数 "in order" 的约定,我们只是不这样做,因为这样做很痛苦。
我目前正在阅读一本关于 C# 编程的书,它简要介绍了上溢和下溢,作者给出了当你超过某种类型的允许范围时会发生什么的一般概念。
示例
short a = 30000;
short b = 30000;
short sum = (short)(a + b); // Explicitly cast back into short
Console.WriteLine(sum); // This would output the value -5536
所以short类型的范围只有-32768到32767,书中作者给出的解释是
"For integer types (byte, short, int, and long), the most significant bits (which overflowed) get dropped. This is especially strange, as the computer then interprets it as wrapping around. This is why we end up with a negative value in our example. You can easily see this happening if you start with the maximum value for a particular type (for example, short.MaxValue
) and adding one to it." C# 玩家指南第二版,第 9 章,第 58 页
你最终会达到最低 (-32768)。
我无法理解这一点当作者谈到 "the computer interprets it as wrapping around"
时我感到困惑我尝试理解的方式是 short 类型使用 2 个字节(16 位)
所以数字 32767 = 0111111111111111 如果我要 +1 到二进制字符串,我最终会得到 32768 = 1000000000000000(不能用 short 类型表示,因为最大值是 32767)所以编译器给出 -32768。为什么最后会变成负数?
我理解使用二进制补码表示负数的概念,有人可以在这里纠正我的想法或详细说明,因为我不完全理解为什么我们只使用 16 位中的 15 位来表示正数范围和负范围的最高有效位
忽略所有告诉你最高位是符号位的人。这是错误的思考方式。
正确的思考方式是:
- 我们有 65536 种可能的位模式。
- 因此我们可以表示 65536 个可能的数字。
- 我们必须有一个 map 为每个位模式分配 意义。
对于无符号短裤,我们按如下方式分配位模式:
0000000000000000 --> 0
0000000000000001 --> 1
...
0111111111111111 --> 32767
1000000000000000 --> 32768
1000000000000001 --> 32769
...
1111111111111111 --> 65535
对于签名短裤,我们使用以下约定:
0000000000000000 --> 0
0000000000000001 --> 1
...
0111111111111111 --> 32767
1000000000000000 --> -32768
1000000000000001 --> -32767
...
1111111111111111 --> -1
就这么简单。
为什么要使用这个约定?
三个原因:
(1) 无论你是有符号的还是无符号的,前 32K 个值都是一样的。太方便了。
(2) 在这两个约定中,"all zero bits" 表示零。
(3) 因为加法在两种约定中的作用完全相同!
我们有
0000000000000000 --> 0
我们想加一个。我们使用二进制规则添加一个,我们得到:
0000000000000001 --> 1
无论短片是有符号还是无符号,这都有效。
我们有无符号空头:
1000000000000000 --> 32768
我们想加一个。我们使用二进制规则这样做,我们得到正确的答案:
1000000000000001 --> 32769
签名短裤也一样。我们有
1000000000000000 --> -32768
我们希望添加一个。我们使用二进制规则这样做,我们得到:
1000000000000001 --> -32767
同样,您可以通过将 1111111111111111
加到任何二进制数来验证您得到 "one less",因此减一和加一一样有效。然后您可以继续证明,一般来说,加法和减法在有符号和无符号算术中的工作方式相同,这意味着处理器不必知道编译器认为这些值是有符号还是无符号.
这就是我们使用二进制补码的原因:因为无论您是在进行有符号运算还是无符号运算,基础数学都是完全相同的。
请注意,我对其中的 "sign bit" 只字未提。高位设置为负数这一事实只是一个不错的奖励。我们想要的真正 属性 是只需要构建硬件来进行数学计算 一次。
二进制补码只是一种约定,用于根据与保存该位模式的变量关联的类型 将两种可能含义之一分配给位模式。作为一个行业,我们选择这个约定是因为制造使用这个约定的高性能硬件很便宜。
我们可以选择许多其他约定。例如,我们可以说对于有符号数,我们使用映射:
0000000000000000 --> -32768
0000000000000001 --> -32767
...
0111111111111111 --> -1
1000000000000000 --> 0
1000000000000001 --> 1
...
1111111111111111 --> 32767
请注意,这与之前完全相同,除了最高位!
在这种解释中,加法仍然像我们预期的那样起作用。但是这个map没有short和ushort前32K值相同的属性,也没有全零位表示0的属性。所以我们不使用这个约定,因为它不太方便。在这个约定中,从 short 转换为 ushort 需要做一个加法。将短路设置为零需要将字节设置为非零的值。我们可以使用带符号数 "in order" 的约定,我们只是不这样做,因为这样做很痛苦。