C 在方程式中溢出?
C overflows inside an equation?
a + b
如我所料将 255 溢出回 4,然后 c / 2
如我所料给出 2。但是为什么最后一个例子在计算相同的两个步骤时不会溢出?
我猜内部计算值存储了更多位,然后在进行赋值时才被截断为 8 位。那么极限在哪里,它一定会在某个时候溢出?
uint8_t a = 250;
uint8_t b = 10;
uint8_t c = (a + b);
uint8_t d = c / 2;
uint8_t e = (a + b) / 2;
std::cout << unsigned(c) << ", " << unsigned(d) << ", " << unsigned(e) << "\n";
4, 2, 130
a+b
给出的值 260 未分配给任何 uint8_t
类型,因此您在最后一种情况下很好。只有当您将任何大于 255 的值分配给 uint8_t
时才会发生溢出。
这叫做积分推广。这些操作本身是在您的 CPU 原生整数类型 int
中完成的,它可以容纳大于 255 的数字。在 a+b
的情况下,结果必须存储在 uint8_t
中,这就是截断完成。在最后一种情况下,首先有一个作为 int
完成的除法,结果可以完美地存储在 uint8_t
.
中
下面的(a + b)
不会溢出,编译器将a
和b
识别为整数类型,所以加法结果是整数类型,这个表达式的结果不受限制根据表达式中项或因子的大小。
让我们假设变量的类型,如 a
或 b
在这种情况下将结果限制为该类型。虽然可能,但几乎不可能使用这样的语言。想象一下五个变量,当不考虑类型时,它们总和为 500,即 this..
uint8_t a = 98;
uint8_t b = 99;
uint8_t c = 100;
uint8_t d = 101;
uint8_t e = 102;
以上变量的总和== 500。现在...在下面任何表达式的结果都不能超过其中一项的大小...
int incorrect = (a + b + c + d + e);
在这种情况下 (a + b + c) == 41
然后 (41 + d + e)
== 244。这是一个荒谬的答案。大多数人都认可的替代方案即
(98 + 99 + 100 + 101 + 102) == 500;
这是存在类型转换的原因之一。
表达式中的中间结果不应受表达式中的项或因子的限制,而应受结果类型(即左值)的限制。
@atturri 是正确的。这是 x86 机器语言中您的变量发生的情况:
REP STOS DWORD PTR ES:[EDI]
MOV BYTE PTR SS:[a],0FA
MOV BYTE PTR SS:[b],0A
MOVZX EAX,BYTE PTR SS:[a] ; promotion to 32bit integer
MOVZX ECX,BYTE PTR SS:[b] ; promotion to 32bit integer
ADD EAX,ECX
MOV BYTE PTR SS:[c],AL ; ; demotion to 8bit integer
MOVZX EAX,BYTE PTR SS:[c]
CDQ
SUB EAX,EDX
SAR EAX,1
MOV BYTE PTR SS:[d],AL
MOVZX EAX,BYTE PTR SS:[a]
MOVZX ECX,BYTE PTR SS:[b]
ADD EAX,ECX
CDQ
SUB EAX,EDX
SAR EAX,1
MOV BYTE PTR SS:[e],AL
a + b
如我所料将 255 溢出回 4,然后 c / 2
如我所料给出 2。但是为什么最后一个例子在计算相同的两个步骤时不会溢出?
我猜内部计算值存储了更多位,然后在进行赋值时才被截断为 8 位。那么极限在哪里,它一定会在某个时候溢出?
uint8_t a = 250;
uint8_t b = 10;
uint8_t c = (a + b);
uint8_t d = c / 2;
uint8_t e = (a + b) / 2;
std::cout << unsigned(c) << ", " << unsigned(d) << ", " << unsigned(e) << "\n";
4, 2, 130
a+b
给出的值 260 未分配给任何 uint8_t
类型,因此您在最后一种情况下很好。只有当您将任何大于 255 的值分配给 uint8_t
时才会发生溢出。
这叫做积分推广。这些操作本身是在您的 CPU 原生整数类型 int
中完成的,它可以容纳大于 255 的数字。在 a+b
的情况下,结果必须存储在 uint8_t
中,这就是截断完成。在最后一种情况下,首先有一个作为 int
完成的除法,结果可以完美地存储在 uint8_t
.
下面的(a + b)
不会溢出,编译器将a
和b
识别为整数类型,所以加法结果是整数类型,这个表达式的结果不受限制根据表达式中项或因子的大小。
让我们假设变量的类型,如 a
或 b
在这种情况下将结果限制为该类型。虽然可能,但几乎不可能使用这样的语言。想象一下五个变量,当不考虑类型时,它们总和为 500,即 this..
uint8_t a = 98;
uint8_t b = 99;
uint8_t c = 100;
uint8_t d = 101;
uint8_t e = 102;
以上变量的总和== 500。现在...在下面任何表达式的结果都不能超过其中一项的大小...
int incorrect = (a + b + c + d + e);
在这种情况下 (a + b + c) == 41
然后 (41 + d + e)
== 244。这是一个荒谬的答案。大多数人都认可的替代方案即
(98 + 99 + 100 + 101 + 102) == 500;
这是存在类型转换的原因之一。
表达式中的中间结果不应受表达式中的项或因子的限制,而应受结果类型(即左值)的限制。
@atturri 是正确的。这是 x86 机器语言中您的变量发生的情况:
REP STOS DWORD PTR ES:[EDI]
MOV BYTE PTR SS:[a],0FA
MOV BYTE PTR SS:[b],0A
MOVZX EAX,BYTE PTR SS:[a] ; promotion to 32bit integer
MOVZX ECX,BYTE PTR SS:[b] ; promotion to 32bit integer
ADD EAX,ECX
MOV BYTE PTR SS:[c],AL ; ; demotion to 8bit integer
MOVZX EAX,BYTE PTR SS:[c]
CDQ
SUB EAX,EDX
SAR EAX,1
MOV BYTE PTR SS:[d],AL
MOVZX EAX,BYTE PTR SS:[a]
MOVZX ECX,BYTE PTR SS:[b]
ADD EAX,ECX
CDQ
SUB EAX,EDX
SAR EAX,1
MOV BYTE PTR SS:[e],AL