这些操作是否相同?

时间:2016-10-04 22:02:29

标签: c# algorithm

我正在重构一些代码,我偶然发现了这样一段话。

if (a > 1.0)
  a = 1.0;
else if (a < -1.0)
  a = -1.0;

根据我们的指导方针,我应该将其重构为类似的东西。

if (Math.Abs(a) > 1.0)
  a = a < 0 ? -1.0 : 1.0;

当然,只有在不改变语句逻辑的情况下才允许重构。我通过它并没有看到任何偏差或任何差异。我还编写并运行了一系列测试,真正试图找出棘手和前卫的案例。一切似乎都很好。

然后,一位正在工作的同事偷看了它,并用一种​​非常有毒的语调暗示那里有什么东西发痒。他应该在第二天揭露这个大秘密,但后来他病了,现在他正在度假。

我几天都在凝视这些台词。我已经睡了。我已经尝试了所有的技巧。没有!所以,要么我只是不熟练看到它,或者他只是一个屁股玩肮脏的技巧。我需要帮助确定。

3 个答案:

答案 0 :(得分:5)

是的,他们是一样的。 更新:但不是案例Int.MinValue \ Long.MinValue等,因为Math.Abs会抛出OverflowException - 感谢@ SledgeHammer / @ Quantic! - 这可能是捕获。

&#34;证明&#34;方法是相同的(减去溢出)

if (Math.Abs(a) > 1.0)
  a = a < 0 ? -1.0 : 1.0;

与:( ?:if

相同
if (Math.Abs(a) > 1.0)
{
    if (a < 0)
    {
        a = -1.0;
    }
    else
    {
        a = 1.0;
    }
}

这与:(写出Math.Abs

相同
if (a > 1.0 || a < -1.0)
{
    if (a < 0)
    {
        a = -1.0;
    }
    else
    {
        a = 1.0;
    }
}

,这与:(将||翻译为if - else if

if (a > 1.0)
{
    if (a < 0)
    {
        a = -1.0;
    }
    else
    {
        a = 1.0;
    }
}
else if (a < -1.0)
{
    if (a < 0)
    {
        a = -1.0;
    }
    else
    {
        a = 1.0;
    }
}

删除死码:

if (a > 1.0)
{
    if (a >= 0) //else
    {
        a = 1.0;
    }
}
else if (a < -1.0)
{
    if (a < 0)
    {
        a = -1.0;
    }
}

现在删除不需要的(内部)if s:

if (a > 1.0)
{
    a = 1.0;
}
else if (a < -1.0)
{
   a = -1.0;
}

完成:)

PS:如果愿意,为了便于阅读:

if (a > 1.0)
  a = 1.0;
else if (a < -1.0)
  a = -1.0;

更新:Math.Abs​​

Math.Abs(a) > 1.0a > 1.0 || a < -1.0

Math.Abs(a) > 1.0

Math.Abs(a)相当于(参见reference source

a >= 0 ? a : -a 

相同
if (a >= 0)
{
    return a;
}
else
{
    return -a;
}

添加了条件:

if (a >= 0)
{
    return a > 1.0;
}
else
{

    return (-a) > 1.0;
}

重写条件:

if (a >= 0)
{
    return a > 1.0;
}
else
{
    return a < -1.0;
}

对于(a >= 0),其他情况永远不会如此,所以

a > 1.0 || a < -1.0

答案 1 :(得分:1)

Julian's answer显示了为什么它们在概念上是相同的,所以我会深入了解内部。

我写了一个小测试程序,然后查看了反汇编(Visual Studio 2012)

class Program
{
    static void A(double a)
    {
        if (a > 1.0)
            a = 1.0;
        else if (a < -1.0)
            a = -1.0;
    }

    static void B(double a)
    {
        if (Math.Abs(a) > 1.0)
            a = a < 0 ? -1.0 : 1.0;
    }

    static void Main(string[] args)
    {
        A(-1.17);
        B(-1.17);
    }
}

A的结果

            if (a > 1.0)
0000002b  fld         qword ptr [ebp+8] 
0000002e  fld1 
00000030  fcomip      st,st(1) 
00000032  fstp        st(0) 
00000034  jp          0000003A 
00000036  jb          0000003E 
00000038  jmp         0000003A 
0000003a  xor         eax,eax 
0000003c  jmp         00000043 
0000003e  mov         eax,1 
00000043  test        eax,eax 
00000045  sete        al 
00000048  movzx       eax,al 
0000004b  mov         dword ptr [ebp-3Ch],eax 
0000004e  cmp         dword ptr [ebp-3Ch],0 
00000052  jne         0000005C 
                a = 1.0;
00000054  fld1 
00000056  fstp        qword ptr [ebp+8] 
00000059  nop 
0000005a  jmp         00000092 
            else if (a < -1.0)
0000005c  fld         qword ptr [ebp+8] 
0000005f  fld         dword ptr ds:[001D2F50h] 
00000065  fcomip      st,st(1) 
00000067  fstp        st(0) 
00000069  jp          0000006F 
0000006b  ja          00000073 
0000006d  jmp         0000006F 
0000006f  xor         eax,eax 
00000071  jmp         00000078 
00000073  mov         eax,1 
00000078  test        eax,eax 
0000007a  sete        al 
0000007d  movzx       eax,al 
00000080  mov         dword ptr [ebp-3Ch],eax 
00000083  cmp         dword ptr [ebp-3Ch],0 
00000087  jne         00000092 
                a = -1.0;
00000089  fld         dword ptr ds:[001D2F58h] 
0000008f  fstp        qword ptr [ebp+8] 

总计:38条说明

B的结果

            if (Math.Abs(a) > 1.0)
0000002b  fld         qword ptr [ebp+8] 
0000002e  sub         esp,8 
00000031  fstp        qword ptr [esp] 
00000034  call        749B481F 
00000039  fstp        qword ptr [ebp-44h] 
0000003c  fld         qword ptr [ebp-44h] 
0000003f  fld1 
00000041  fcomip      st,st(1) 
00000043  fstp        st(0) 
00000045  jp          0000004B 
00000047  jb          0000004F 
00000049  jmp         0000004B 
0000004b  xor         eax,eax 
0000004d  jmp         00000054 
0000004f  mov         eax,1 
00000054  test        eax,eax 
00000056  sete        al 
00000059  movzx       eax,al 
0000005c  mov         dword ptr [ebp-3Ch],eax 
0000005f  cmp         dword ptr [ebp-3Ch],0 
00000063  jne         0000008A 
                a = a < 0 ? -1.0 : 1.0;
00000065  fld         qword ptr [ebp+8] 
00000068  fldz 
0000006a  fcomip      st,st(1) 
0000006c  fstp        st(0) 
0000006e  jp          00000072 
00000070  ja          0000007A 
00000072  nop 
00000073  fld1 
00000075  fstp        qword ptr [ebp-4Ch] 
00000078  jmp         00000083 
0000007a  fld         dword ptr ds:[001D3008h] 
00000080  fstp        qword ptr [ebp-4Ch] 
00000083  nop 
00000084  fld         qword ptr [ebp-4Ch] 
00000087  fstp        qword ptr [ebp+8]

总计:36条指令+对Math.Abs​​的函数调用

<强>结果: 第一个可能要快一点,但是它们的尺寸非常接近,很难想象性能实际上会因使用其中一个而受到严重影响的情况。我个人同意您的原始版本在概念上更容易理解的评论。

编辑由于上面的其他评论,似乎主要区别在于是否可以从Math.Abs抛出异常或者原始版本吞没异常。看起来您正在使用double,文档会使no mention of exceptions on the double versionthey do for the int version一样。我仍然支持你的原始版本。

答案 2 :(得分:0)

为什么不

a = Math.Min(Math.Abs(a), 1.0) * Math.Sign(a);

假设您对Math操作有FPU支持,由于clean pipeline和更好branch prediction ...因为没有分支,上述内容可能会更快执行,因为我们删除了所有if语句。当然,这完全依赖于硬件,并且您无法通过检查IL来减少代码大小。你只需要知道&#34;这有点好。在某些情况下。

请教导您额外学分:)