我正在重构一些代码,我偶然发现了这样一段话。
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;
当然,只有在不改变语句逻辑的情况下才允许重构。我通过它并没有看到任何偏差或任何差异。我还编写并运行了一系列测试,真正试图找出棘手和前卫的案例。一切似乎都很好。
然后,一位正在工作的同事偷看了它,并用一种非常有毒的语调暗示那里有什么东西发痒。他应该在第二天揭露这个大秘密,但后来他病了,现在他正在度假。我几天都在凝视这些台词。我已经睡了。我已经尝试了所有的技巧。没有!所以,要么我只是不熟练看到它,或者他只是一个屁股玩肮脏的技巧。我需要帮助确定。
答案 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(a) > 1.0
到a > 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
version像they 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;这有点好。在某些情况下。
请教导您额外学分:)