使整数为偶数

发布于 2024-10-12 20:07:03 字数 730 浏览 2 评论 0原文

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

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;

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

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

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

Sometimes I need to be sure that some integer is even. As such I could use the following code:

int number = /* magic initialization here */;

// make sure the number is even
if ( number % 2 != 0 ) {
    number--;
}

but that does not seem to be very efficient the most efficient way to do it, so I could do the following:

int number = /* magic initialization here */;

// make sure the number is even
number &= ~1;

but (besides not being readable) I am not sure that solution is completely portable.

  • Which solution do you think is best?
  • Is the second solution completely portable?
  • Is the second solution considerably faster that the first?
  • What other solutions do you know for this problem?
  • What if I do this inside an inline method? It should (theoretically) be as fast as these solutions and readability should no longer be an issue, does that make the second solution more viable?

note: This code is supposed to only work with positive integers but having a solution that also works with negative numbers would be a plus.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

猫烠⑼条掵仅有一顆心 2024-10-19 20:07:03

就我个人而言,我会选择内联辅助函数。

inline int make_even(int n)
{
    return n - n % 2;
}

// ....

int m = make_even(n);

Personally, I'd go with an inline helper function.

inline int make_even(int n)
{
    return n - n % 2;
}

// ....

int m = make_even(n);
浅浅 2024-10-19 20:07:03

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

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

  1. if (数字 % 2 != 0) {
    数字 - ;
    }
  2. number&= ~1
  3. number = number - (number % 2);
  4. number = (number / 2) * 2; >

在继续之前,让我澄清一些事情:
使用任何这些方法的预期收益都是最小的,即使我们可以
证明一种方法比其他方法快 200% 最差的方法也快
获得可见的速度增益的唯一方法是如果该方法是
在 CPU 密集型应用程序中多次调用。因此,这更多的是
锻炼是为了好玩,而不是真正的优化。

分析

可读性

就可读性而言,我将方法 1 列为最具可读性的,
方法 4 是第二好的,方法 2 是最差的。
人们可以自由地提出不同意见,但我这样对它们进行排名,因为:

  1. 方法 1 中,尽可能明确的是,如果数字是奇数,则
    想要从中减去使其均匀。
  2. 方法 4 也非常明确,但我将其排在第二位,因为
    乍一看,您可能会认为它什么也没做,只是完成了一小部分
    第二个后者你就像“哦......整数除法。”。
  3. 方法2在可读性方面几乎等同于方法2,但是很多
    人们不习惯按位运算,因此我将方法 2 列为
    越糟糕。

考虑到这一点,我想补充一点,人们普遍认为最好的方法是
实现这一点是使用内联函数,并且没有任何选项是
不可读,可读性并不是真正的问题(直接在代码中使用
明确且清晰,阅读该方法永远不会那么困难)。

如果您不想使用 inline 方法,我建议您只使用
方法 1方法 4

兼容性问题

下溢

有人提到方法 1 可能会下溢,具体取决于方法 1 的方式
处理器代表整数。只是为了确保您可以添加以下内容
使用方法 1 时的STATIC_ASSERT

STATIC_ASSERT(INT_MIN % 2 == 0, make_even_may_underflow);

至于方法3,即使INT_MIN不是偶数,也可能不会下溢
取决于结果是否与除数或除数具有相同的符号
股利。具有相同符号的除数永远不会下溢,因为
INT_MIN - (-1) 更接近 0。
添加以下 STATIC_ASSERT 只是为了确保:

STATIC_ASSERT(INT_MIN % 2 == 0 || -1 % 2 < 0, make_even_may_underflow);

当然,当 STATIC_ASSERT 失败时,您仍然可以使用这些方法,因为
仅当您将 INT_MIN 传递给 make_even 方法时才会出现问题,
但我强烈建议反对。

(不)支持的位表示

当使用方法 2 时,您应该确保您的编译器位表示
表现如预期:

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); 

速度

我还使用 Unix time 实用程序进行了一些简单的速度测试。我跑了每
不同的方法(及其变体)4次并记录结果
由于结果差异不大,我认为没有必要进行更多测试。

获得的结果显示方法4方法2作为其中最快的
全部。

结论

根据所提供的信息,我建议使用方法4。它是
可读,我不知道任何兼容性问题并且性能很好。

我希望您喜欢这个答案并使用此处包含的信息来做出
您自己的明智选择。 :)


如果您想检查我的结果,可以使用源代码。请注意
测试使用 g++ 编译并在 Mac OS X 中运行。不同
平台和编译器可能会给出不同的结果。

Before accepting an answer I will make my own that tries to summarize and
complete some of the information found here:

Four possible methods where described (and some small variations of these).

  1. if (number % 2 != 0) {
    number--;
    }
  2. number&= ~1
  3. number = number - (number % 2);
  4. number = (number / 2) * 2;

Before proceeding any further let me clarify something:
The expected gain for using any of these methods is minimal, even if we could
prove that one method is 200% faster than the others the worst one is so fast
that the only way to have visible gain in speed would be if this method was
called many times in a CPU bound application. As such this is more of an
exercise for fun than a real optimization.

Analysis

Readability

As far as readability goes I would rank method 1 as the most readable,
method 4 as the second best and method 2 as the worse.
People are free to disagree but I ranked them like this because:

  1. In method 1 it is as explicit as possible that if the number is odd you
    want to subtract from it making it even.
  2. Method 4 is also very much explicit but I ranked it second because at
    first glance you might think it is doing nothing, and only a fraction of a
    second latter you're like "Oh... Integer division.".
  3. Method 2 and 3 are almost equivalent in terms of readability, but many
    people are not used to bitwise operations and as such I ranked method 2 as
    the worse.

With that in mind I would add that it is generally accepted that the best way
to implement this is using an inline function, and none of the options is
that unreadable, readability is not really an issue (direct uses in the code
are explicit and clear and reading the method will never be that hard).

If you don't want to use an inline method I would recommend that you only use
method 1 or method 4.

Compatibility issues

Underflow

It has been mentioned that method 1 may underflow, depending on the way the
processor represents integers. Just to be sure you can add the following
STATIC_ASSERT when using method 1.

STATIC_ASSERT(INT_MIN % 2 == 0, make_even_may_underflow);

As for method 3, even when INT_MIN is not even it may not underflow
depending on whether the result has the same sign of the divisor or the
dividend. Having the same sign of the divisor never underflows because
INT_MIN - (-1) is closer to 0.
Add the following STATIC_ASSERT just to be sure:

STATIC_ASSERT(INT_MIN % 2 == 0 || -1 % 2 < 0, make_even_may_underflow);

Of course you can still use these methods when the STATIC_ASSERT fails since
it would only be a problem when you pass INT_MIN to your make_even method,
but I would STRONGLY advice against it.

(Un)supported bit representations

When using method 2 you should make sure your compiler bit representation
behaves as expected:

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); 

Speed

I also did some naive speed tests using the Unix time utility. I ran every
different method (and its variations) 4 times and recorded the results,
since the results didn't vary much I didn't find necessary to run more tests.

The obtained results show method 4 and method 2 as the fastest of them
all.

Conclusion

According to the provided information, I would recommend using method 4. Its
readable, I am not aware of any compatibility issues and performs great.

I hope you enjoy this answer and use the information contained here to make
your own informed choice. :)


The source code is available if you want to check my results. Please note
that the tests where compiled using g++ and run in Mac OS X. Different
platforms and compilers may give different results.

番薯 2024-10-19 20:07:03
int even_number = (number / 2) * 2;

只要优化器不妨碍(它不应该,但谁知道),无论架构如何,这都应该有效。

int even_number = (number / 2) * 2;

This should work regardless architecture as long as optimizer is not going in the way (it shouldn't but who knows).

听,心雨的声音 2024-10-19 20:07:03

我会使用第二种解决方案。在任何二进制表示中,无论位数、大尾数法还是小尾数法或其他架构差异,该操作都会产生将最低位设置为零的效果。它速度快且完全便携。如果您遇到无法理解代码含义的可怜的 C 程序员,可以通过注释来解释代码的意图。

I would use the second solution. In any binary representation, regardless of the number of bits, big-endian vs. little-endian, or other architecture differences, that operation will have the effect of setting the lowest bit to zero. It's fast and completely portable. The intent of the code can be explained via comments, if you meet any poor C programmers who can't figure out what it means.

粉红×色少女 2024-10-19 20:07:03

&= 解决方案对我来说看起来最好。如果您想让它更便携且更具可读性:

const int MakeEven = -2;

int number = /* magic initialization here */
// Make sure number is even
number &= MakeEven;

第二个解决方案应该比第一个解决方案快得多。它完全便携吗?最有可能的是,尽管某处可能有一些计算机以不同的方式进行数学计算。

这应该适用于正整数和负整数。

The &= solution looks best to me. If you want to make it more portable and more readable:

const int MakeEven = -2;

int number = /* magic initialization here */
// Make sure number is even
number &= MakeEven;

The second solution should be considerably faster than the first. Is it completely portable? Most likely, although there's probably some computer somewhere that does math differently.

This should work for positive and negative integers.

小嗲 2024-10-19 20:07:03

使用第二个解决方案作为内联函数,并将静态断言放入它的实现中,以记录和测试它是否在编译它的平台上工作。

 BOOST_STATIC_ASSERT( (1 & ~1) == 0 );

 BOOST_STATIC_ASSERT( (-1 & ~1) == -2 );

Use your second solution as inline function and put static assert into implementation of it to document and test that it works on platform that it is compiled on.

 BOOST_STATIC_ASSERT( (1 & ~1) == 0 );

 BOOST_STATIC_ASSERT( (-1 & ~1) == -2 );
旧街凉风 2024-10-19 20:07:03

仅当您的符号表示形式是“二进制补码”或“符号和大小”时,您的第二个解决方案才有效。为了就地做到这一点,我会使用 suszterpatt 的变体,它应该(几乎)总是有效。

number -= (number % 2);

您不确定这将在哪个方向上“舍入”负值,因此在极端情况下,您可能会出现下溢。

Your second solution only works if your sign representation is "two's complement" or "sign and magnitude". To do it in place I'd go with suszterpatt's variant, which should (almost) always work

number -= (number % 2);

You don't know for sure in which direction this will "round" for negative values, so in extreme cases you might have an underflow.

只想待在家 2024-10-19 20:07:03
even_integer = (any_integer >> 1) << 1;

与其他建议的解决方案相比,该解决方案以最高效的方式实现了目标。
一般来说,按位移位是最便宜的操作。某些编译器也会为“number = (number / 2) * 2”生成相同的程序集,但不能保证在所有目标平台和编程语言上都如此。

even_integer = (any_integer >> 1) << 1;

This solution achieves the goal in the most performant way compared to the other suggested solutions.
In general, bitwise shift is the cheapest possible operation. Some compilers generate the same assembly for "number = (number / 2) * 2" as well but that is not guaranteed on all target platforms and programming languages.

雪化雨蝶 2024-10-19 20:07:03

以下方法很简单,不需要乘法或除法。

number = number & ~1;

或者

number = (number + 1) & ~1;

The following approach is simple and requires no multiplication or division.

number = number & ~1;

or

number = (number + 1) & ~1;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文