有时我需要确定某个整数是偶数。因此,我可以使用以下代码:
int number = /* magic initialization here */;
// make sure the number is even
if ( number % 2 != 0 ) {
number--;
}
但这似乎不是非常有效最有效的方法,所以我可以做到以下几点:
int number = /* magic initialization here */;
// make sure the number is even
number &= ~1;
但是(除了不可读)我不确定解决方案完全是否可移植。
注意:此代码应该只适用于正整数,但有一个解决方案也适用于负数将是一个加号。
答案 0 :(得分:31)
就个人而言,我会使用内联辅助函数。
inline int make_even(int n)
{
return n - n % 2;
}
// ....
int m = make_even(n);
答案 1 :(得分:6)
我会使用第二种解决方案。在任何二进制表示中,无论位数,big-endian与little-endian或其他架构差异如何,该操作都会将最低位设置为零。它快速且完全便携。如果遇到任何无法弄清楚它意味着什么的可怜的C程序员,可以通过评论来解释代码的意图。
答案 2 :(得分:6)
int even_number = (number / 2) * 2;
只要优化器不妨碍(不应该但是谁知道),这应该适用于任何架构。
答案 3 :(得分:4)
在接受答案之前,我会自己尝试总结和答案 完成这里找到的一些信息:
描述了四种可能的方法(以及这些方法的一些小变化)。
if (number % 2 != 0) {
number--;
}
number&= ~1
number = number - (number % 2);
number = (number / 2) * 2;
在继续进行之前,让我澄清一下: 使用任何这些方法的预期收益很小,即使我们可以 证明一种方法比其他方法快200%,最差的方法如此之快 只有这种方法才能获得速度可见增益的唯一方法 在CPU绑定的应用程序中多次调用。因此,这更像是一个 运动是为了娱乐,而不是真正的优化。
就可读性而言,我会将方法1 排在最可读的位置, 方法4 作为第二好,方法2 更差。 人们可以自由地反对,但我将它们排列为这样,因为:
考虑到这一点,我想补充说,人们普遍认为这是最好的方法
实现这一点是使用inline
函数,没有选项
那个难以理解的可读性并不是一个真正的问题(在代码中直接使用)
明确而清晰,阅读方法永远不会那么难。
如果您不想使用inline
方法,我建议您仅使用
方法1 或方法4 。
有人提到方法1 可能会下溢,具体取决于方式 处理器代表整数。只是为了确保您可以添加以下内容 使用方法1时<{1}}。。
STATIC_ASSERT
至于方法3 ,即使STATIC_ASSERT(INT_MIN % 2 == 0, make_even_may_underflow);
不均匀,也可能不会下溢
取决于结果是否与除数或符号相同
股利。具有相同的除数符号永远不会下降,因为
INT_MIN
接近0。
添加以下INT_MIN - (-1)
只是为了确保:
STATIC_ASSERT
当然STATIC_ASSERT(INT_MIN % 2 == 0 || -1 % 2 < 0, make_even_may_underflow);
失败后,你仍然可以使用这些方法
将INT_MIN传递给STATIC_ASSERT
方法只会出现问题,
但我强烈建议反对它。
使用方法2 时,应确保编译器位表示 表现如预期:
make_even
我还使用Unix STATIC_ASSERT( (1 & ~1) == 0, unsupported_bit_representation);
// two's complement OR sign-and-magnitude.
STATIC_ASSERT( (-3 & ~1) == -4 || (-3 & ~1) == -2 , unsupported_bit_representation);
实用程序进行了一些天真的速度测试。我跑了
不同的方法(及其变化)4次并记录results,
由于结果变化不大,我没有必要进行更多测试。
获得的results show 方法4 和方法2 是最快的 所有
根据提供的信息,我建议使用方法4 。它的 可读,我不知道任何兼容性问题,并且表现很好。
我希望您喜欢这个答案,并使用此处包含的信息 你自己明智的选择。 :)
如果您想查看我的搜索结果,source code可用。请注意
使用time
编译并在Mac OS X中运行的测试。不同
平台和编译器可能会给出不同的结果。
答案 4 :(得分:3)
&=
解决方案对我来说最好。如果你想让它更便携和更易读:
const int MakeEven = -2;
int number = /* magic initialization here */
// Make sure number is even
number &= MakeEven;
第二种解决方案应该比第一种解决方案快得多。是完全便携式吗?最有可能的是,尽管某些计算机可能会以不同的方式进行数学运算。
这适用于正整数和负整数。
答案 5 :(得分:3)
将第二个解决方案用作内联函数,并将静态断言放入其实现中,以记录并测试它是否在编译它的平台上运行。
BOOST_STATIC_ASSERT( (1 & ~1) == 0 );
BOOST_STATIC_ASSERT( (-1 & ~1) == -2 );
答案 6 :(得分:1)
您的第二个解决方案仅适用于您的签名表示是“两个补码”或“符号和幅度”。要做到这一点,我会选择suszterpatt的变体,它应该(几乎)始终有效
number -= (number % 2);
您无法确定哪个方向会为负值“舍入”,因此在极端情况下您可能会出现下溢。
答案 7 :(得分:-1)
even_integer = (any_integer >> 1) << 1;
与其他建议的解决方案相比,此解决方案以最高效的方式实现了目标。 一般来说,按位移位是最便宜的操作。一些编译器也为“number = (number / 2) * 2”生成相同的程序集,但并不能保证在所有目标平台和编程语言上都如此。
答案 8 :(得分:-3)
以下方法很简单,不需要乘法或除法。
number = number & ~1;
或
number = (number + 1) & ~1;