这些操作是等价的吗?
本文关键字:操作 | 更新日期: 2023-09-27 18:11:36
我正在重构一些代码,我偶然发现了这样一段。
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;
当然,只有在不改变语句逻辑的情况下才允许重构。我仔细考虑过了,没有发现任何偏差或不符之处。我还编写并运行了一些测试,试图找出那些棘手和棘手的情况。一切似乎都很好。
然后,一个同事偷看了一眼,用一种非常恶毒的语气说那里有什么东西很痒。他本来应该在第二天公布这个大秘密的,但后来他病了,现在正在度假。
我已经盯着这行好几天了。我已经考虑过了。我已经试过了所有的办法。没有什么!所以要么是我看不懂要么就是他是个玩弄卑鄙把戏的混蛋。
是的,它们是一样的。更新:但不是Int.MinValue
' Long.MinValue
等情况,因为Math.Abs
会抛出OverflowException
-感谢@SledgeHammer/@Quantic!-所以这可能是陷阱。
方法相同的"证明"(除去溢出)
if (Math.Abs(a) > 1.0)
a = a < 0 ? -1.0 : 1.0;
等于:(?:
to 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;
更新:数学。Abs
from Math.Abs(a) > 1.0
to a > 1.0 || a < -1.0
Math.Abs(a) > 1.0
Math.Abs(a)
等价于(参见参考来源)
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;
}
else对于(a >= 0)
不为真,所以
a > 1.0 || a < -1.0
Julian的回答说明了为什么它们在概念上是相同的,所以我将深入研究。
我写了一个小测试程序,然后查看了反汇编(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
结果:第一个可能稍微快一点,但是它们的大小非常接近,很难想象使用其中一个会严重影响性能的情况。我个人同意你的原始版本在概念上更容易理解的评论。
Edit看起来,多亏了上面的其他评论,主要的区别在于异常是否可以从Math.Abs
抛出或被原始版本吞噬。看起来你使用的是double
,但是文档没有提到double
版本上的异常,就像int
版本一样。我仍然支持你原来的版本
为什么不
a = Math.Min(Math.Abs(a), 1.0) * Math.Sign(a);
假设您有FPU支持Math
操作,由于干净的管道和更好的分支预测,上述操作可能执行得更快…因为没有分支,因为我们删除了所有的if
语句。当然,这完全取决于硬件,并且您无法通过检查IL看到代码大小的减少。您只需要"知道"这是更好的。在某些情况下。
把这个告诉你的老师以获得额外的学分:)