C# 将数字加倍-左移与乘法

C# 将数字加倍-左移与乘法,c#,.net,algorithm,C#,.net,Algorithm,两者的区别是什么 int size = (int)((length * 200L) / 100L); // (1) 及 int size=length这听起来像是过早的优化。只做length*2的好处是编译器肯定会对此进行优化,并且更易于维护。这里是第三个选项: int size = length * 2; // Comment explaining what is 2 or what means this multiplication 这一定是最好的选择。因为它可读性强,容易理解你想做什么

两者的区别是什么

int size = (int)((length * 200L) / 100L); // (1)


int size=length这听起来像是过早的优化。只做length*2的好处是编译器肯定会对此进行优化,并且更易于维护。

这里是第三个选项:

int size = length * 2; // Comment explaining what is 2 or what means this multiplication
这一定是最好的选择。因为它可读性强,容易理解你想做什么。 至于性能,编译器正在生成非常优化的代码,因此不必担心这样一个简单的操作。 如果您对溢出有任何顾虑,可以使用
checked
block


EDIT正如许多其他人所提到的,这里只使用任何有意义的变量,而不是
2

对于任何现代编译器,左移1或乘以2将生成相同的代码。它可能是一个add指令,将数字添加到自身中,但这取决于您的目标


另一方面,执行(x*200)/100与将数字加倍并不相同,因为第一次乘法可能会溢出,因此这无法安全地优化为将数字加倍。

对于普通程序员来说,哪个更容易阅读:

int size = length * 2;
int size = length << 1;
int size=length*2;

int size=length200L和100L代表什么?在我看来,你正在使用。你应该试着用一种尽可能好地描述其意图的方式来编写你的程序;使其尽可能可读。为这些值使用命名常量而不是幻数是一种很好的方法。此外,在它自己的命名方法中提取计算也有帮助。当你这样做的时候,你会立即看到没有办法将它重写成
x这个想法
有趣的是,大多数答案都认为编译器会将乘以2的幂优化为位移位。很明显,没有一个响应程序真正尝试编译位移位和乘法,以查看编译器实际生成的内容

这纯粹是一项学术活动;正如几乎每个人都指出的那样,乘法更容易理解(尽管有人猜测“*200L/100L”部分的确切原因——这只会让事情变得混乱)。同样非常明显的是,在C#中用位移位代替乘法不会有任何显著的性能提高,即使在紧循环中也是如此。如果您需要这种优化,那么您一开始就使用了错误的平台和语言

让我们看看当我们使用VisualStudio2010附带的CSC(C#编译器)编译一个简单程序并启用优化时会发生什么。这是第一个节目:

static void Main(string[] args)
{
    int j = 1;

    for (int i = 0; i < 100000; ++i)
    {
        j *= 2;
    }
}
这是第二个节目:

static void Main(string[] args)
{
    int j = 1;

    for (int i = 0; i < 100000; ++i)
    {
        j <<= 1;
    }
}
注意第8行的差异。程序的第一个版本使用乘法(mul),而第二个版本使用左移位(shl)


我不确定JIT在执行代码时对它做了什么,但C编译器本身显然没有优化二次幂乘到位移位。

为了回答您的实际问题,这两段代码之间似乎没有行为差异,当
length
为非负
int
且代码文本位于未选中的
上下文中时

在选中的
上下文中
(int.MaxValue*200L)
永远不会溢出,因为结果很容易适合
长的
(int)((length*200L)/100L
仅当
length*2
溢出时才会溢出。在任何情况下,移位运算符都不会溢出。因此,在
选中的
上下文中,代码片段的行为会有所不同

如果
length
为负,则移位操作将产生不正确的结果,而乘法将正常工作。因此,如果允许
length
变量为负,则两个代码片段不同

(与流行的观点相反,当我看到这个成语时:

int val = (someval * someConstant) / someOtherConstant;
我认为该代码的目的是以某种方式进行缩放。它可以是手动定点代码,也可以避免整数除法的问题。具体示例:

int val = someVal * (4/5); // oops, val = 0 - probably not what was intended
或编写以避免来回使用浮点:

int val = (int)(someVal * .8); // better than 0 - maybe we wanted to round though - who knows?
当我看到这个成语:

int val = someVal << someConstant;

有一种无限的方法来创建EXPR,使得Vall总是具有相同的值。重要的是要考虑你在ExPR中表达的意图对于那些将遵循的人。

< P>不管代码可读性:位移位和整数乘法,即使由两个恒定的幂,常常是不相同的。
没有编译器会将
x*2
优化为
x我不确定,但我认为,现代编译器能够将这两个语句优化为(几乎)同样的。我认为
length*2'实际上比第一个选项更糟糕。你的建议对代码意图的描述更少。有第四个选项,我认为更好。他应该使用命名常量,而不是
200L`和'100L'。这在描述代码意图方面做得更好。第四个选项的优势是什么?如果有关于溢出的问题,检查块就足够了。@Incognito:对不起,我的评论不是很清楚。我不认为性能不是问题,因为编译器会优化。你的选择和我的一样。你评论可读性,我同意可读性非常重要,但不要认为
length*2
I这是最可读的。进行这种计算可能是有原因的。因此,我认为使用命名常量对性能更好。顺便说一句,我有点急于否决你。你能编辑你的评论吗。这是我撤消否决投票的唯一方法。@Everyone:如果有人不知道
length*2
是什么,他们有brain伤害。关于可读性的这一点是一堆废话。任何东西乘以任何整数都非常容易理解:将“thing”乘以
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] int32 j,
           [1] int32 i)
  IL_0000:  ldc.i4.1
  IL_0001:  stloc.0
  IL_0002:  ldc.i4.0
  IL_0003:  stloc.1
  IL_0004:  br.s       IL_000e
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.2
  IL_0008:  mul
  IL_0009:  stloc.0
  IL_000a:  ldloc.1
  IL_000b:  ldc.i4.1
  IL_000c:  add
  IL_000d:  stloc.1
  IL_000e:  ldloc.1
  IL_000f:  ldc.i4     0x186a0
  IL_0014:  blt.s      IL_0006
  IL_0016:  ret
} // end of method Program::Main
static void Main(string[] args)
{
    int j = 1;

    for (int i = 0; i < 100000; ++i)
    {
        j <<= 1;
    }
}
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       23 (0x17)
  .maxstack  2
  .locals init ([0] int32 j,
           [1] int32 i)
  IL_0000:  ldc.i4.1
  IL_0001:  stloc.0
  IL_0002:  ldc.i4.0
  IL_0003:  stloc.1
  IL_0004:  br.s       IL_000e
  IL_0006:  ldloc.0
  IL_0007:  ldc.i4.2
  IL_0008:  shl
  IL_0009:  stloc.0
  IL_000a:  ldloc.1
  IL_000b:  ldc.i4.1
  IL_000c:  add
  IL_000d:  stloc.1
  IL_000e:  ldloc.1
  IL_000f:  ldc.i4     0x186a0
  IL_0014:  blt.s      IL_0006
  IL_0016:  ret
} // end of method Program::Main
int val = (someval * someConstant) / someOtherConstant;
int val = someVal * (4/5); // oops, val = 0 - probably not what was intended
int val = (int)(someVal * .8); // better than 0 - maybe we wanted to round though - who knows?
int val = someVal << someConstant;
int val = expr;
int x = 2000000000, multiplied, shifted;
// Intention is to store results in 64-bit in case x is large:
long multA, shiftA, multB, shiftB;
checked
{
shifted = x << 1; // Overflows to negative number.
multiplied = x * 2; // Throws OverflowException.
shiftA = x << 1; // Maybe this overflows to negative number too?
multA = x * 2; // Maybe this throws OverflowException too?
shiftB = x << 1L; // Happens to double OK, because x is positive.
multB = x * 2L; // Works regardless of the value of x.
}