无符号数学是否需要更多CPU指令?

Lig*_*ica 6 c++ cpu unsigned

取一个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用于有符号值.