如果我想将一个无符号整数舍入到最接近的小整数或相等的偶数整数,我可以除以2然后乘以2?
例如 :
f(8)=8 f(9)=8
我可以做x = x/2*2;
? 编译器是否有优化这种expression的风险?
只要编译器不会在程序中引入任何副作用,编译器就可以进行任何优化。 在你的情况下,它不能取消'2',那么expression式将有一个奇数不同的值。
x / 2 * 2
被严格地评估为(x / 2) * 2
,并且如果x
是整数types,则在整数算术中执行x / 2
。
实际上,这是一种惯用的四舍五入技术。
既然你指定整数是无符号的,你可以用一个简单的掩码来完成:
x & (~1u)
这将把LSB设置为零,从而产生不大于x
的即时偶数。 也就是说,如果x
的types不比unsigned int
宽。
你当然可以强制1
与更宽的x
types相同,如下所示:
x & ~((x & 1u) | 1u)
但是在这一点上,你真的应该把这个方法看作是混淆的练习,接受拔示巴的回答。
我当然忘了标准库。 如果你包含stdint.h
(或cstdint
,就像你在C ++代码中那样)。 你可以让实现来处理细节:
uintmax_t const lsb = 1; x & ~lsb;
要么
x & ~UINTMAX_C(1)
C和C ++通常在优化中使用“as if”规则。 计算结果必须好像编译器没有优化你的代码。
在这种情况下, 9/2*2=8
。 编译器可以使用任何方法来实现结果8.这包括位掩码,位移,或任何CPU特定的破解产生相同的结果(x86有相当多的技巧,依靠它不区分指针和整数,不像C和C ++)。
您可以编写x / 2 * 2
,如果x
具有无符号types,编译器将生成非常高效的代码以清除最低有效位。
相反,你可以写:
x = x & ~1;
或者可能不太可读:
x = x & -2;
甚至
x = (x >> 1) << 1;
或者这也是:
x = x - (x & 1);
或者超级猫提出的最后一个,它适用于所有整数types和表示的正值:
x = (x | 1) ^ 1;
上述所有提议都可以在2的补码体系结构上正确运行所有无符号整数types。 编译器是否会产生最佳代码是configuration和实现质量的问题。
请注意,如果x
的types大于unsigned int
,则x & (~1u)
不起作用。 这是一个反直觉的陷阱。 如果你坚持使用一个无符号常量,那么你必须写x & ~(uintmax_t)1
因为即使x & ~1ULL
types比unsigned long long
大, x & ~1ULL
x & ~(uintmax_t)1
也会失败。 更糟的是,现在很多平台的整数types都比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所build议的那样,在Godbolt的Compiler Explorer上进行的testing显示,对于上述所有替代scheme, gcc -O1
-O1为unsigned int
生成相同的确切代码,但将T
types更改为unsigned long long
显示test1u
不同代码。
如果你的值是任何无符号types,就像你说的那样,最简单的是
x & -2;
无符号算术的奇迹使得-2
被转换为x
的types,并且具有全1的位模式,但是对于最低有效位0
。
与其他一些build议的解决scheme相反,这应该适用于任何至less与unsigned
一样宽的unsigned
符号整数types。 (不pipe怎样,你不应该用较窄的types进行算术)。
额外的奖金,如supercat所说,这只能使用签名types转换为无符号types。 这是由标准定义为模算术。 所以UTYPE
的结果总是UTYPE_MAX-1
是x
的无符号types。 具体而言,它与签名types的平台的符号表示无关。
我感到惊讶的一个选项到目前为止还没有被提到使用模运算符。 我认为这至less和你原来的片段一样,意味着你的意图,甚至更好。
x = x - x % 2
正如其他人所说,编译器的优化器将等价地处理任何合理的expression式,所以担心什么更清晰,而不是你认为最快的。 所有这些调整的答案都是有趣的,但是你绝对不应该用它们中的任何一个来代替算术运算符(假设动机是算术而不是微调)。
只需使用以下:
template<class T> inline T f(T v) { return v & (~static_cast<T>(1)); }
不要害怕这是函数,编译器最终应该把这个优化成适当types为1的v&(〜1)。