哪个更好的select用于除以2的整数?

以下哪种技术是将整数除以2的最佳select,为什么?

技术1:

x = x >> 1; 

技术2:

 x = x / 2; 

这里x是一个整数。

使用最能描述你正在做什么的操作。

  • 如果您将数字视为一系列位,请使用bitshift。
  • 如果您将其视为数值,请使用除法。

请注意,它们并不完全相同。 他们可以给负面的整数不同的结果。 例如:

 -5 / 2 = -2 -5 >> 1 = -3 

(ideone)

第一个看起来像分裂吗? 不可以。如果你想分割,使用x / 2 。 如果可能的话,编译器可以对其进行优化以使用位移(这就是所谓的强度降低),如果你自己做,它就是无用的微型优化。

要堆积如山:有很多理由赞成使用x = x / 2; 这里有一些:

  • 它更清楚地expression了你的意图(假设你不是在处理寄存器位的事情)

  • 编译器会把这个减less到一个移位操作

  • 即使编译器没有减less它,而是select了一个比shift更慢的操作,那么最终影响到程序性能的可能性本身就是微乎其微的(如果它确实影响了它,那么你就有一个实际的理由使用换class)

  • 如果该部门将成为一个更大的expression式的一部分,如果使用除法运算符,则更有可能获得优先权:

     x = x / 2 + 5; x = x >> 1 + 5; // not the same as above 
  • 有符号的算术可能会比上面提到的优先级问题更复杂

  • 重申 – 无论如何,编译器已经为你做了这个。 事实上,它将把常数转换为一系列的转换,增加和乘以各种数字,而不仅仅是两个幂。 看到这个问题的链接,甚至更多的信息。

简而言之,如果你真的想要乘法或者除法,那么你可以通过编码来购买任何东西,除了增加引入错误的可能性。 由于编译器不够聪明,在适当的时候将这种事情优化到一个class次,这是一辈子的。

哪一个是最好的select,为什么把整数除以2?

取决于你最好的意思。

如果你想要你的同事恨你,或者让你的代码难以阅读,我一定会select第一个选项。

如果你想把一个数字除以2,那么去第二个数字。

两者不是等价的,如果数字是负数或者在较大的expression式内部,它们的行为不一样 – bitshift的优先级低于+- ,分区的优先级更高。

你应该写你的代码来expression它的意图是什么。 如果性能是你关心的,不用担心,优化器在这些微观优化方面做得很好。

只要使用分( / ),假设它更清晰。 编译器会相应地进行优化。

我同意你应该赞成x / 2其他答案,因为它的意图更清晰,编译器应该为你优化它。

然而,偏好x / 2 x >> 1 x / 2不是x >> 1另一个原因是,如果x是一个有符号int并且是负数,那么>>的行为是依赖于实现的。

ISO C99标准第5部分第6.5.7节:

E1 >> E2的结果是E1右移E2位的位置。 如果E1具有无符号types,或者如果E1具有带符号types和非负值,则结果的值是E1 / 2 E2的商的整数部分。 如果E1有签名types和负值,则结果值是实现定义的。

x / 2更清晰, x >> 1速度并不快(根据微基准testing,Java JVM的速度提高了约30%)。 正如其他人所指出的,对于负数,四舍五入略有不同,所以当你想处理负数时你必须考虑这个问题。 一些编译器可能会自动将x / 2转换为x >> 1如果他们知道这个数字不能是负数(甚至认为我无法validation这一点)。

即使x / 2不能使用(慢)除CPU指令,因为有些快捷方式是可能的 ,但它仍然比x >> 1慢。

(这是一个C / C ++问题,其他编程语言有更多的运算符,对于Java也有无符号的右移x >>> 1 ,这又是不同的,它允许正确计算两个值,所以(a + b) >>> 1即使对于ab非常大的值也会返回平均值,例如对于二进制search,如果数组索引可以变得非常大,就需要这个值。 许多版本的二进制search ,因为他们用(a + b) / 2来计算平均值,这是行不通的,正确的解决方法是用(a + b) >>> 1来代替。

Knuth说:

不成熟的优化是万恶之源。

所以我build议使用x /= 2;

这样的代码很容易理解,而且我认为这种forms的优化操作并不意味着处理器的巨大差异。

看看编译器输出来帮助你决定。 我在x86-64上运行了这个testing
gcc(GCC)4.2.1 20070719 [FreeBSD]

另请参见godbolt上的在线编译器输出 。

你所看到的是编译器在两种情况下都使用sarl (算术右移)指令,所以它确实可以识别两个expression式之间的相似性。 如果使用除法,编译器也需要调整负数。 为此,将符号位向下移动到最低位,并将其添加到结果中。 这样在修改负数时就可以解决掉一个问题,相比之下,这个分歧会起什么作用。
既然分裂情况确实有两个变化,而显性变化情况只有一个变化,现在我们可以解释一下其他答案测量的一些性能差异。

带有汇编输出的C代码:

对于分歧,你的意见是

 int div2signed(int a) { return a / 2; } 

这编译到

  movl %edi, %eax shrl $31, %eax addl %edi, %eax sarl %eax ret 

类似的转变

 int shr2signed(int a) { return a >> 1; } 

输出:

  sarl %edi movl %edi, %eax ret 

只是一个附加说明 –

在一些基于虚拟机的语言中,x * = 0.5通常会更快 – 特别是actionscript,因为variables不需要被除数0检查。

使用x = x / 2; OR x /= 2; 因为今后有可能是一个新的程序员在工作。 因此,他会更容易地找出代码行中发生了什么。 每个人都可能不知道这样的优化。

我说的是编程比赛的目的。 一般来说,他们有非常大的input,其中除以2发生多次,并知道input是正面还是负面。

x >> 1会比x / 2更好。 我在ideone.com上运行了一个程序,在这个程序中,有两个操作发生了10 ^ 10个分区。 x / 2花了将近5.5s,而x >> 1则花了将近2.6s。

我会说有几件事情要考虑。

  1. Bitshift应该更快,因为实际上并不需要特殊的计算来移位,但正如所指出的那样,负数有潜在的问题。 如果你确保有正数,并且正在寻找速度,那么我会推荐移位。

  2. 师的操作人员很容易阅读。 所以如果你正在寻找代码的可读性,你可以使用它。 请注意,编译器优化领域已经走了很长的路,所以使代码易于阅读和理解是很好的做法。

  3. 根据底层硬件,操作可能有不同的速度。 安达尔的法则是使普通案件迅速。 所以你可能有硬件可以执行不同的操作比其他更快。 例如,乘以0.5可能会快于除以2.(如果您希望强制执行整数除法,您可能需要采取乘法的底线)。

如果你是在纯粹的performance后,我会build议创build一些testing,可以做数百万次的操作。 对你的OS /硬件/编译器/代码进行多次抽样(你的样本量),以确定哪一个在统计上最好。

就CPU而言,位移操作比分割操作快。 然而,编译器知道这一点,并将在可能的范围内进行适当的优化,因此,您可以采用最有意义的方式进行编码,并且知道您的代码正在高效运行。 但是请记住, unsigned int可以(在某些情况下)比int更好地优化,因为之前指出的原因。 如果你不需要签名算术,那么不要包含符号位。

x = x / 2; 是合适的代码使用..但一个操作取决于你自己的程序如何输出你想产生。

让你的意图更清楚…例如,如果你想分裂,使用X / 2,并让编译器优化它移动运营商(或其他任何东西)。

今天的处理器不会让这些优化对程序的性能产生任何影响。

答案将取决于你所处的环境。

  • 如果你正在使用一个8位微控制器或者没有硬件支持乘法的任何东西,那么位移就是预料之中的,而且很平常,编译器几乎肯定会把x /= 2变成x >>= 1 ,符号会在这种环境中引起更多的眉毛,而不是使用换class来实现分工。
  • 如果您正在关键性能的环境或代码段中工作,或者您的代码可以通过编译优化来编译, x >>= 1 ,并且解释其推理的注释可能是为了清晰起见。
  • 如果你不符合上述条件之一,只需使用x /= 2来使代码更易读。 更好地保存下一个程序员,看看你的代码10秒钟双重你的转换操作比不必要地certificate你知道这个转变是更有效的sans编译器优化。

所有这些都假定无符号整数。 简单的转变可能不是你想要签名的。 另外,DanielH提出了一些关于如ActionScript使用x *= 0.5的好处。

mod 2,test for = 1。不要c中的语法。 但这可能是最快的。

一般右移分为:

 q = i >> n; is the same as: q = i / 2**n; 

这有时被用来加速程序的清晰度。 我不认为你应该这样做。 编译器足够聪明,可以自动执行加速。 这意味着, 以明确的代价来进行转变并不会带来任何益处

从实用的C ++编程看看这个页面。

显然,如果你正在为下一个阅读代码的人编写代码,那么清楚的是“x / 2”。

但是,如果速度是您的目标,请尝试两种方式和时间的结果。 几个月前我曾经研究过一个位图卷积例程,它涉及到一个整数数组,并将每个元素除以2.我做了各种各样的事情来优化它,包括用“x >> 1”代替“x / 2" 。

当我实际计时两种方式时,我惊奇地发现x / 2比x >> 1更快

这是使用Microsoft VS2008 C ++打开默认的优化。

在性能方面。 CPU的移位操作比划分操作码快得多。 因此,除以2或乘以2等都将从换class中受益。

至于外观和感觉。 作为工程师,我们变得如此依恋化妆品,即使美丽的女士们也不会使用! 🙂

X / Y是一个正确的…和“>>”移位运算符。如果我们想要两个整除我们可以使用(/)分配算子。 移位运算符用于移位。

X = X / 2; X / = 2; 我们可以这样使用

虽然x >> 1比x / 2更快,但在处理负值时正确使用>>要复杂一点。 它需要类似于以下内容:

 // Extension Method public static class Global { public static int ShiftDivBy2(this int x) { return (x < 0 ? x + 1 : x) >> 1; } }