看一下编译器生成的x86程序集,我注意到(无符号)整数除法有时实现为整数乘法.这些优化似乎遵循这种形式
value / n => (value * ((0xFFFFFFFF / n) + 1)) / 0x100000000
Run Code Online (Sandbox Code Playgroud)
例如,执行除以9:
12345678 / 9 = (12345678 * 0x1C71C71D) / 0x100000000
Run Code Online (Sandbox Code Playgroud)
除以3将使用乘法0x55555555 + 1,依此类推.
利用mul指令将结果的高部分存储在edx寄存器中的事实,可以使用具有魔术值的单个乘法来获得除法的最终结果.(虽然这种优化有时与最后的逐位移位一起使用.)
我想了解一下这实际上是如何运作的.这种方法什么时候有效?为什么必须将1添加到我们的"神奇数字"中?
optimization assembly bit-manipulation multiplication division
请考虑以下代码(在C++ 11中):
int a = -11, b = 3;
int c = a / b;
// now c == -3
Run Code Online (Sandbox Code Playgroud)
C++ 11规范称负股息的除法向零舍入.
对于有一个运算符或函数来进行除向负无穷大的舍入非常有用(例如,为了在迭代范围时与正红利保持一致),那么标准库中是否有一个函数或运算符可以满足我的需要?或者也许是在现代编译器中执行它的编译器定义的函数/内在函数?
我可以编写自己的,例如以下(仅适用于正数除数):
int div_neg(int dividend, int divisor){
if(dividend >= 0) return dividend / divisor;
else return (dividend - divisor + 1) / divisor;
}
Run Code Online (Sandbox Code Playgroud)
但它不会像我的意图那样描述,也可能不是标准库函数或编译器内在优化(如果存在).
很多人经常在对右移运算符的讨论中指出C标准明确指出右移负数的影响是实现定义的.鉴于C编译器已被用于为不使用二进制补码算法的各种平台生成代码,我可以理解该语句的历史基础.然而,我所知道的所有新产品开发都围绕着处理器,这些处理器除了二进制补码之外没有任何类型的整数运算的固有支持.
如果代码希望通过二的幂执行难倒符号整数除法,它只会为当前或未来的架构中运行,没有任何现实的危险,即未来的编译器会解释右移操作员做还要别的吗?如果存在现实可能性,是否有任何好的方法来提供它而不会对可读性,性能或两者产生负面影响?是否有任何其他依赖关系可以证明对操作符的行为进行彻底的假设(例如,代码在不支持函数X的实现上将是无用的,并且如果实现不使用符号扩展的右移,则实现不太可能支持X )?
注意:我要求根据C99和C11标签,因为我希望更新的语言功能将成为支持的平台之一,这意味着平台可能会使用右移,这在算术上等同于分区,并且有兴趣了解任何以其他方式实现右移的C99或C11编译器.