使整数均匀

时间:2011-01-19 18:53:49

标签: c++ c integer bit-manipulation

有时我需要确定某个整数是偶数。因此,我可以使用以下代码:

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;

但是(除了不可读)我不确定解决方案完全是否可移植。

  • 您认为哪种解决方案最好?
  • 第二种解决方案是否完全可移植?
  • 第二个解决方案是否比第一个解决方案快得多?
  • 您对此问题还有哪些其他解决方案?
  • 如果我在内联方法中执行此操作会怎样?它(理论上)应该与这些解决方案一样快,可读性不再是问题,这是否会使第二种解决方案更可行?

注意:此代码应该只适用于正整数,但有一个解决方案也适用于负数将是一个加号。

9 个答案:

答案 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)

在接受答案之前,我会自己尝试总结和答案 完成这里找到的一些信息:

描述了四种可能的方法(以及这些方法的一些小变化)。

  1. if (number % 2 != 0) { number--; }
  2. number&= ~1
  3. number = number - (number % 2);
  4. number = (number / 2) * 2;
  5. 在继续进行之前,让我澄清一下: 使用任何这些方法的预期收益很小,即使我们可以 证明一种方法比其他方法快200%,最差的方法如此之快 只有这种方法才能获得速度可见增益的唯一方法 在CPU绑定的应用程序中多次调用。因此,这更像是一个 运动是为了娱乐,而不是真正的优化。

    分析

    可读性

    就可读性而言,我会将方法1 排在最可读的位置, 方法4 作为第二好,方法2 更差。 人们可以自由地反对,但我将它们排列为这样,因为:

    1. 方法1 中,如果数字是奇数,则尽可能明确 想要减去它,使它变得均匀。
    2. 方法4 也非常明确但我把它排在第二,因为at 乍一看,你可能认为它什么都不做,只有一小部分 第二个后者你就像“哦......整数分裂。”。
    3. 方法2 和3在可读性方面几乎相同,但很多 人们不习惯按位操作,因此我将方法2 排名为 更糟糕的。
    4. 考虑到这一点,我想补充说,人们普遍认为这是最好的方法 实现这一点是使用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方法只会出现问题, 但我强烈建议反对它。

      (Un)支持位表示

      使用方法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;