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.
}