例如 :
f(8)=8
f(9)=8
我可以x = x/2*2;吗?编译器是否有可能优化掉此类表达式?
例如 :
f(8)=8
f(9)=8
我可以x = x/2*2;吗?编译器是否有可能优化掉此类表达式?
x >>= 1; x <<= 1;
x &= ~(1)' Where the 1`可以是所需长度的1的字符串。例如:x &= ~(0x07);对于三位,等等
Answers:
编译器可以进行喜欢的任何优化,只要它不会在程序中引入任何副作用即可。在您的情况下,它不能取消“ 2”,因为表达式的奇数值将有所不同。
x / 2 * 2如果是整数类型,则严格按计算(x / 2) * 2,并x / 2以整数算术执行x。
实际上,这是惯用的舍入技术。
(x>>1)<<1。IIRC,即使技术上关闭了优化,也有编译器会执行此操作。
(x & 0xfffffffe):)
由于您指定的整数是无符号的,因此可以使用简单的掩码进行操作:
x & (~1u)
这会将LSB设置为零,从而产生不大于的立即偶数x。也就是说,ifx的类型不超过unsigned int。
当然,您可以强制使其1与Wideer具有相同的类型x,如下所示:
x & ~((x & 1u) | 1u)
但是,在这一点上,您确实应该将这种方法视为混淆处理,并接受Bathsheba的回答。
我当然忘记了标准库。如果包含stdint.h(或cstdint,则应在C ++代码中使用)。您可以让实现处理细节:
uintmax_t const lsb = 1;
x & ~lsb;
要么
x & ~UINTMAX_C(1)
x & (~static_cast<decltype(x)>(1))还是我需要咖啡?
UINT_MAX,不?我想如果x是的话unsigned long,还是会在同一条船上。
x & (~1u)如果类型x大于,恐怕无法正常工作unsigned int。相反,x & ~1对于所有类型,其行为均符合预期。这是违反直觉的陷阱。如果您坚持使用无符号常量,则必须编写,x & ~(uintmax_t)1因为x & ~1ULL如果x类型大于,甚至会失败unsigned long long。更糟糕的是,许多平台现在具有比更大的整数类型uintmax_t,例如__uint128_t。
C和C ++通常在优化中使用“好像”规则。计算结果必须好像编译器没有优化您的代码。
在这种情况下,9/2*2=8。编译器可以使用任何方法来获得结果8。这包括位掩码,位移位或产生相同结果的任何特定于CPU的破解(x86的许多技巧都依赖于它不能区分指针的事实。和整数,与C和C ++不同)。
-O1。所有方法都被提炼为相同的机器代码。
and RAX, -2甚至都会在上使用该方法-O0。
x/2*2编译为单个语句。通常,未经优化的编译是出于调试目的,然后您需要源代码和汇编之间的1:N对应关系。
您可以编写,x / 2 * 2并且如果x具有无符号类型,则编译器将产生非常有效的代码来清除最低有效位。
相反,您可以这样写:
x = x & ~1;
或者可能不太可读:
x = x & -2;
甚至
x = (x >> 1) << 1;
还是这个:
x = x - (x & 1);
或由supercat建议的最后一个方法,适用于所有整数类型和表示形式的正值:
x = (x | 1) ^ 1;
以上所有建议对于2的补码体系结构上的所有无符号整数类型均正常工作。编译器是否会生成最佳代码是配置和实现质量的问题。
但是请注意,x & (~1u)如果类型x大于则不起作用unsigned int。这是违反直觉的陷阱。如果您坚持使用无符号常量,则必须编写,x & ~(uintmax_t)1因为x & ~1ULL如果x类型大于,甚至会失败unsigned long long。更糟糕的是,许多平台现在都具有大于的整数类型uintmax_t,例如__uint128_t。
这是一个小基准:
typedef unsigned int T;
T test1(T x) {
return x / 2 * 2;
}
T test2(T x) {
return x & ~1;
}
T test3(T x) {
return x & -2;
}
T test4(T x) {
return (x >> 1) << 1;
}
T test5(T x) {
return x - (x & 1);
}
T test6(T x) { // suggested by supercat
return (x | 1) ^ 1;
}
T test7(T x) { // suggested by Mehrdad
return ~(~x | 1);
}
T test1u(T x) {
return x & ~1u;
}
正如Ruslan所建议的那样,在Godbolt的Compiler Explorer上进行的测试表明,上述所有替代方案都为gcc -O1产生了完全相同的代码unsigned int,但是将类型更改T为unsigned long long显示了不同的代码test1u。
x。您能证明它不适用什么价值吗?
1为正,并且始终表示为00...01。|1设置LSB。^1切换同一位。一个补码会影响MSB的值,INT_MIN但不会影响MSB的值。IOW,当您将算术和数值运算混合使用时,数字表示形式开始变得很重要,而我们不这样做。(但问题在于如何将负数四舍五入,向下舍入或舍入为零)
x = x - (x%2)?这具有可扩展到任何n地方的优势roundToMultiple(int x, int n) { return x - (x % n); }
如果您所说的值是任何无符号类型,最简单的方法是
x & -2;
无符号算术的奇妙之处在于将其-2转换为的类型x,并具有一个全为1的位模式,但最低有效位为0。
与其他一些建议的解决方案相反,此方法应与至少等于的任何无符号整数类型一起使用unsigned。(而且,无论如何,您都不应该对较窄的类型进行算术运算。)
如supercat所述,额外的奖励仅使用将带符号类型转换为无符号类型。标准已将其定义为模算术。因此,结果始终UTYPE_MAX-1是UTYPE的无符号类型x。特别是,它与用于签名类型的平台的符号表示无关。
到目前为止,我还没有提到的一种选择是使用模运算符。我认为这至少与您的原始代码片段一样代表您的意图,甚至可能更好。
x = x - x % 2
正如其他人所说的那样,编译器的优化器将等效地处理任何合理的表达式,因此请担心更清楚的内容,而不是您认为最快的内容。所有令人费解的答案都是有趣的,但是您绝对不应使用其中的任何一个代替算术运算符(假设动机是算术而不是微调)。
x % d = x - x / d * d,因此我的代码段可以保证与x = x/2*2问题中提到的结果相同。假设x您担心自己会被否定(而不是对的否定替代2),这将给出OP可能想要的:if x=-7then x/2*2 == x - x%2 == -6。
x/2*2使用负整数,OP也很令人惊讶。因此,不确定如何避免使用负整数的人表现出令人惊讶的代码!