双数-左移与乘法的区别intsize=(int)((length*200L)/100L);//(1)andintsize=length<<1;//(2)(在这两种情况下长度都是int)我假设这两个代码片段都想将长度参数加倍。我喜欢使用(2)...那么使用(1)有什么好处吗?我查看了溢出发生时的边缘情况,两个版本似乎具有相同的行为。请告诉我我错过了什么。这里是第三个选项:intsize=length*2;//注释解释什么是2或者这个乘法是什么意思这一定是最好的选择。因为它是可读的,而且很容易理解你想做什么。至于性能,编译器正在生成相当优化的代码,所以不需要担心这么简单的操作。如果您对溢出有任何顾虑,可以使用checked块。编辑正如许多其他人提到的那样,只需使用任何有意义的变量而不是2。它比乘法更快的想法是因为.NETjit编译器实际上是1970年代编写的优化程度较低的C编译器。就算是真的,这个时间点的差别也是皮秒级的,就算有差别,也不一定有。编写代码以提高可读性。让编译器处理微优化。根据分析真实场景优化代码,而不是事后猜测编译器将生成什么。此外,移位运算符的语义与乘法不同。例如,考虑以下编辑序列:吉尔的原始程序:intx=y*2;Bob的编辑:愚蠢的Jill,我会让这个“更快”:intx=y实习生Larry编辑:哦,我们有一个错误,一个一个地解决这个问题:intx=y和Larry刚刚推出了一个新错误。y*2+1withy我在实际的现场生产代码中看到过这个错误。很容易陷入“转移就是乘法”的心态,而忘记转移的优先级低于加法,而乘法的优先级更高。我从未见过有人通过写x*2得到乘以2的算术优先级错误。人们理解+和*的优先级。很多人忘记了转型的优先次序。您实际上没有节省的皮秒是否值得任何数量的潜在错误?我拒绝。这对普通程序员来说更具可读性:intsize=length*2;intsize=length除非他们来自强大的C++位扭曲背景,否则我敢打赌你的普通程序员会知道第一行是什么(它甚至有“2”代表“double”)但必须停止并暂停第二行线。事实上,我倾向于评论第二行,解释它的作用,当您可以让代码像第一行一样进行对话时,这似乎是多余的。有趣的是,大多数答案都假定编译器将乘以2的幂以进行位移优化。显然,没有一个响应者真正尝试编译位移位而不是乘法以查看编译器实际产生的结果。这纯粹是一个学术练习;正如几乎每个人都指出的那样,乘法更容易阅读(尽管“*200L/100L”部分存在的原因是任何人的猜测——它只是为了混淆事物)。很明显,在C#中用位移位替换乘法不会带来任何显着的性能提升,即使在紧密循环中也是如此。如果您需要这种优化,则说明您使用了错误的平台和语言。让我们看看当我们使用启用了优化的VisualStudio2010附带的CSC(C#编译器)编译一个简单程序时会发生什么。这是第一个程序:staticvoidMain(string[]args){intj=1;for(inti=0;i使用ildasm反编译生成的可执行文件为我们提供了以下CIL列表:.methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged{.entrypoint//Codesize23(0x17).maxstack2.localsinit([0]int32j,[1]int32i)IL_0000:ldc.i4.1IL_0001:stloc.0IL_0002:ldc.i4.0IL_0003:stloc.1IL_0004:br.sIL_000eIL_0006:ldloc。0IL_0007:ldc.i4.2IL_0008:mulIL_0009:stloc.0IL_000a:ldloc.1IL.i4.1IL_000c:添加IL_000d:stloc.1IL_000e:ldloc.1IL_000f:ldc.i40x186a0IL_0014:blt.sIL_0006IL_0016:ret}//方法结束Program::Main这是第二个程序:staticvoidMain(string[]args){intj=1;for(inti=0;i反编译这给我们以下CIL清单:.methodprivatehidebysigstaticvoidMain(string[]args)cilmanaged{.entrypoint//代码大小23(0x17).maxstack2.localsinit([0]int32j,[1]int32i)IL_0000:ldc.i4.1IL_0001:stloc.0IL_0002:ldc.i4.0IL_0003:stloc.1IL_0004:br.sIL_000eIL_0006:ldloc.0IL_0007:ldc.i4.2IL_0008:shlIL_0009:stloc.0IL_000a:ldloc.1IL_000b:ldc.i4.1IL_000c:添加IL_000d:stloc.1IL_000e:ldloc.1IL_004a0x1:blt.sIL_0006IL_0016:ret}//方法结束Program::Main请注意,第8行的差异程序的第一个版本使用乘法(mul),而第二个版本使用左移(shl)。JIT在执行代码时对它做了什么我不确定,但C#编译器本身可以证明乘以2的幂乘以位移没有优化。200L和100L分别代表什么?在我看来,您正在使用神奇的数字。您应该尝试以尽可能最好地描述其意图的方式编写您的程序;使其尽可能可读。为这些值使用命名常量而不是幻数是一个很好的方法。在自己的命名方法中提取计算也有帮助。执行此操作时,您会立即看到它不能重写为x。不是因为结果会不同,而是因为可维护性会受到影响。当你写类似x的代码时,下一个程序员不知道那到底是什么意思,它每分钟都会添加著名的WTF:alttexthttp://sofzh.miximages.com/c%23/wtfm.jpg我认为你的代码应该看起来更像这样:intsize=CalculateSizeOfThing(length);privatestaticintCalculateSizeOfThing(intlength){constlongTotalArraySize=200L;constlongBytesPerElement=100L;返回(长度*TotalArraySize)/BytesPerElement;当然,这些const值的名称是一个疯狂的猜测:-)这听起来像是过早的优化。只做length*2的好处是编译器肯定会把它优化掉,而且更容易维护。当我看到成语时:intval=(someval*someConstant)/someOtherConstant;我认为代码的目的是以某种方式扩展。可以是手动定点代码,也可以避免整数除法的问题。一个具体的例子:intval=someVal*(4/5);//糟糕,val=0-可能不是为了避免浮点来回而设计或编写的:intval=(int)(someVal*.8);//比0好-也许我们想四舍五入-谁知道呢?当我看到这个成语:intval=someVal时,我只是想知道someVal中的各个位是否有更深层次的含义需要移位,然后我开始查看周围的上下文以了解您为什么要这样做。要记住的是,当您编写具有以下内容的代码时:intval=expr;有无数种方法可以创建expr,使val始终具有相同的值。重要的是要考虑您在expr中为将要关注它的人表达的意图。左移1或乘以2将为任何现代编译器生成相同的代码。它可能是一条加法指令,将数字加到自身,但这取决于您的目标。另一方面,执行(x*200)/100与将数字加倍不同,因为第一个乘法可能会溢出,因此不能安全地优化为将数字加倍。要回答您的实际问题,当长度为非负int并且代码文本处于未经检查的上下文中时,这两个代码片段之间似乎没有行为差异。在经过检查的上下文中(int.MaxValue*200L)永远不会溢出,因为结果很容易适合long(int.MaxValue*200L)。(int)((length*200L)/100L只有在length*2溢出时才会溢出。在任何情况下,移位运算符都不会溢出。因此,代码片段在检查上下文中的行为不同。如果length为负,则移位操作将产生不正确的结果,而乘法将正确工作。因此,如果允许长度变量为负数,则两个代码片段是不同的。(与普遍看法相反,x是一个无符号整数,它们是一样的。)以上都是C#学习教程:加倍数-左移与乘法分享,如果对大家有用需要进一步了解C#学习教程,希望大家多多关注---本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
