取一个C++积分变量i,并假设你将它的值乘以2.
如果i有签名,我认为该操作在某种程度上是等效的,至少在数学上是:
i = i << 1;
Run Code Online (Sandbox Code Playgroud)
但是如果i类型是无符号的,那么因为无符号值不会溢出但是以模块的范围执行,大概是操作是这样的:
i = (i << 1) & (decltype(i))-1;
Run Code Online (Sandbox Code Playgroud)
现在,我认为实际的机器指令可能比乘法的一系列移位更简洁.但是现代的,比如x86,CPU是否会对unsigned/modulo数学有特定的指令?或者,与带有符号值的数学相比,使用无符号值执行数学运算往往会花费额外的指令?
(是的,在编程时关心这一点是荒谬的;我对纯粹的好奇感兴趣.)
Nil*_*nck 12
正如其他人已经写过的那样:对CPU来说无关紧要.有符号和无符号指令需要相同的时间,无符号算术中的某些操作甚至更容易做,并且可能需要比带符号变量小的周期(多精度除法就是一个例子).
然而,这只是故事的一半.
C++将有符号整数溢出定义为未定义行为,将无符号整数定义为modulo2.这提供了完全不同的优化机会,导致不同的代码.
一个例子:
int foo (int a)
{
return a * 1000 / 500;
}
unsigned bar (unsigned a)
{
return a * 1000 / 500;
}
Run Code Online (Sandbox Code Playgroud)
这里foo可以优化为:
int foo (int a)
{
return a * 2;
}
Run Code Online (Sandbox Code Playgroud)
和酒吧将保持不变.
请注意,数学上这两个函数是相同的,但如果参数超过INT_MAX/1000,它们会开始给出不同的结果.
由于签名溢出的影响未定义,编译器可以选择在简化表达式时假装没有INT_MAX.对于无符号算术,情况并非如此,编译器必须发出执行乘法和除法的代码.这当然比优化的变体慢.
注意:大多数编译器在进行此类优化时都是保守的,只有在您要求它们时才启用它们,因为它们往往会破坏代码和溢出检查.其他编译器,尤其是嵌入式和DSP世界中的编译器,即使在低优化级别也始终进行这些优化.为这类机器编写的程序员都知道细微的细节,所以这很少成为问题.
OTOH我们已经讨论过C/C++程序员在stackoverflow上不止一次陷入这个陷阱的故事.
int*_*jay 11
不,它不需要更多指令,至少在x86上.
对于某些操作(例如加法和减法),相同的指令用于有符号和无符号类型.这是可能的,因为当使用带符号值的2的补码表示时它们的工作方式相同.
左移也没有区别:最左边的位被硬件简单地丢弃,并且在你的例子中不需要按位执行.
对于其他操作(例如右移),有单独的指令:无符号值的SHR(右移)和有符号值的SAR(移位算术右),保留符号位.
对于有符号/无符号乘法和除法,还有单独的指令:MUL/IMUL和DIV/IDIV,其中IMUL和IDIV用于有符号值.