无符号数学需要更多的CPU指令吗?
Does unsigned math require more CPU instructions?
取一个c++积分变量i
,并假设将其值乘以2。
如果i
有签名,我相信这个操作在某种程度上相当于,至少在数学上,等于:
i = i << 1;
但是如果i
的类型是无符号的,那么由于无符号值不会溢出,而是对它们的范围进行模运算,大概操作是这样的:
i = (i << 1) & (decltype(i))-1;
现在,我认为实际的机器指令可能比乘法的移位序列更简洁。但是,现代的CPU,比如x86,会有针对无符号/模运算的特定指令吗?或者,与使用有符号值进行数学运算相比,使用无符号值进行数学运算是否会花费额外的指令?
(是的,在编程时关心这个是荒谬的;)
正如其他人已经写的那样:这对CPU无关紧要。有符号指令和无符号指令花费的时间相同,无符号算术中的一些操作甚至更容易完成,可能比有符号算术中的一些操作需要更少的周期(多精度除法就是一个例子)。
然而,这只是故事的一半。
c++将有符号整数溢出定义为未定义行为,将无符号整数定义为modulo2。这提供了完全不同的优化机会,从而产生不同的代码。
一个例子:
int foo (int a)
{
return a * 1000 / 500;
}
unsigned bar (unsigned a)
{
return a * 1000 / 500;
}
这里foo可以优化为:
int foo (int a)
{
return a * 2;
}
bar保持不变
请注意,这两个函数在数学上是相同的,但如果参数超过INT_MAX/1000,它们开始给出不同的结果。
由于有符号溢出的影响是未定义的,编译器在简化表达式时可以选择假装没有INT_MAX。对于无符号算术,情况并非如此,编译器必须发出执行乘法和除法的代码。这当然比优化后的版本要慢。
注意:大多数编译器在进行此类优化时都是保守的,只有在您要求时才启用它们,因为它们往往会破坏代码和溢出检查。其他编译器,特别是嵌入式和DSP领域的编译器,即使在较低的优化级别上也总是进行这种优化。为这类机器编写程序的程序员知道细微的细节,所以这很少是一个问题。
OTOH我们已经讨论过C/c++程序员在stackoverflow上不止一次陷入这个陷阱的故事。
不,它不需要更多指令,至少在x86上。
对于某些操作(例如加法和减法),有符号和无符号类型都使用相同的指令。这是可能的,因为它们在使用2的补码表示有符号值时工作相同。
左移也没有区别:最左边的位直接被硬件丢弃,不需要按位执行操作——就像在您的示例中一样。
对于其他操作(例如右移),有单独的指令:SHR(右移)用于无符号值,SAR(右移算术)用于有符号值,保留符号位。
对于有符号/无符号的乘法和除法也有单独的指令:MUL/IMUL和DIV/IDIV,其中IMUL和IDIV用于有符号值。
我想你把它弄错了:在无符号数据类型上,位移位完全按照它在tin上所说的做,未占用的位被0填充。这会对类型的值进行正确的模算术运算,用于左移,即乘法。右移没有算术上的类比因为Z/nZ一般不是除法环也没有除法的概念;右移只是截断除法。
另一方面,有符号类型存在歧义,因为将位模式解释为有符号整数有不同的方法。在2的补码上进行左移,您将得到乘法所期望的"绕行",但是没有右移行为的规范选择。在旧的C标准中,我相信这是完全实现定义的,但我认为C99使这种行为具体化。假设环绕式溢出,哪个是最(全部)?CPU的算术指令对两个的补码硬件做反正,<<
对于无符号类型的乘法是等效的。因此,唯一的问题是,当你对一个小于它所使用的寄存器的类型进行算术运算时,
算术表达式中提升到至少int
(或unsigned int
)的规则基本上是为了避免这种情况发生:当你将unsigned short
乘以2时,结果是int
(如果short
和int
大小相同,则为unsigned int
)。无论是哪种类型,当寄存器大小与类型匹配时,都不需要进行任何模数运算。对于2的补码,无论如何,你不需要不同的指令来进行有符号和无符号的c++乘法运算:除非硬件提供了一个溢出位并且你关心它的值(-1 * 2将是一个无符号溢出,但不是一个有符号溢出,即使结果的位模式是相同的),否则环绕溢出覆盖两者。
唯一可能需要掩码的时候是当你将结果转换回unsigned short
时。即便如此,我认为,谨慎的实现有时可以在int
大小的寄存器顶部留下额外的"无关"位,用于保存中间unsigned short
值。你知道那些额外的顶部位不会影响加法、乘法或减法的模结果,如果值被存储到内存中,它们将被掩盖(假设一条指令将int
大小的寄存器的底部2个字节存储到2个字节的内存中,模量基本上是免费的)。所以这个实现必须小心地在除法、右移、比较和其他我忘记的东西之前进行掩码,或者使用适当的指令。
据我所知,大多数cpu在硬件上都有无符号操作,我很确定x86有。
Unsigned math没有溢出,因此隐式地对它们各自的范围取模。
interjay的回答涵盖了基本内容。更多细节:
现在,我认为实际的机器指令可能会比乘法的移位序列更简洁。
这取决于处理器。在过去,当晶体管很昂贵的时候,像6500和6800这样的处理器每次只能向左或向右移动一位指令。
后来,当芯片变得更大,在一个操作码中有更多的比特用于参数时,"桶移位器"被实现,它可以在一个周期内移动任意数量的比特。这是现代cpu使用的。
或者,与有符号值的数学运算相比,使用无符号值进行数学运算是否会花费额外的指令?
从来没有。
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 无法编译 rtmidi 测试 cmidiin.cpp 文件, 非法指令
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- C++:对不存在的命名空间使用命名空间指令
- 函数名是c中该函数的第一条指令的地址吗
- 在模拟器中使用并集来模拟CPU寄存器有多合适
- 错误:无效的预处理指令 #i 的意思是 #if?
- 您选择的 CPU 不支持 x86-64 指令集
- 假设CPU的解码指令
- 任何可以在单个 CPU 指令中在 0 和 1 之间翻转位/整数/布尔值的可能代码
- 如何拦截发送到CPU的指令
- 用于减小CPU指令大小的C++技术
- 如何优化因 CPU 分支未命中而减慢的跳转指令
- 如何控制 Microsoft C 运行时库使用的 CPU 指令
- 编译器是如何知道我的CPU的指令集的
- 线性代数的CPU指令集
- 无符号数学需要更多的CPU指令吗?
- CPU指令与持续时间
- 如何检查CPU是否支持SSE3指令集
- CPU 指令重新排序