在一些数字代码中,我注意到为x86编译或使用AnyCPU +“首选32位”编译时的调试和发布版本产生了不同的结果。 我已经将代码分解成几乎可以重现该问题的最低限度的代码。 事实证明,在其中一个计算步骤中只有1位发生了变化。
代码:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim tau = 0.000001
Dim a = 0.5
Dim value2 = (2 * Math.PI * 0.000000001 * tau) ^ a * Math.Sin(a * (Math.PI / 2))
RichTextBox1.Text = BitConverter.DoubleToInt64Bits(value2).ToString
End Sub
在Int64中,Debug版本给出4498558851738655340
,而Release版本给出4498558851738655341
(请注意最后一位)。
我尝试在调试版本中手动启用优化功能(以及类似的DEBUG常量和pdb生成),但结果保持不变。仅将完整的构建类型更改为Release才能更改结果。
再进一步,我尝试比较Telerik的JustDecompile给出的IL:
调试版本:
.method private instance void Button1_Click (
object sender,
class [mscorlib]System.EventArgs e
) cil managed
{
.locals init (
[0] float64 V_0,
[1] float64 V_1,
[2] float64 V_2,
[3] int64 V_3
)
IL_0000: nop
IL_0001: ldc.r8 1E-06
IL_000a: stloc.0
IL_000b: ldc.r8 0.5
IL_0014: stloc.1
IL_0015: ldc.r8 6.2831853071795863E-09
IL_001e: ldloc.0
IL_001f: mul
IL_0020: ldloc.1
IL_0021: call float64 [mscorlib]System.Math::Pow(float64, float64)
IL_0026: ldloc.1
IL_0027: ldc.r8 1.5707963267948966
IL_0030: mul
IL_0031: call float64 [mscorlib]System.Math::Sin(float64)
IL_0036: mul
IL_0037: stloc.2
IL_0038: ldarg.0
IL_0039: callvirt instance class [System.Windows.Forms]System.Windows.Forms.RichTextBox CETestGenerator.Form1::get_RichTextBox1()
IL_003e: ldloc.2
IL_003f: call int64 [mscorlib]System.BitConverter::DoubleToInt64Bits(float64)
IL_0044: stloc.3
IL_0045: ldloca.s V_3
IL_0047: call instance string [mscorlib]System.Int64::ToString()
IL_004c: callvirt instance void [System.Windows.Forms]System.Windows.Forms.RichTextBox::set_Text(string)
IL_0051: nop
IL_0052: ret
}
发布版本:
.method private instance void Button1_Click (
object sender,
class [mscorlib]System.EventArgs e
) cil managed
{
.locals init (
[0] float64 V_0,
[1] float64 V_1,
[2] float64 V_2,
[3] int64 V_3
)
IL_0000: ldc.r8 1E-06
IL_0009: stloc.0
IL_000a: ldc.r8 0.5
IL_0013: stloc.1
IL_0014: ldc.r8 6.2831853071795863E-09
IL_001d: ldloc.0
IL_001e: mul
IL_001f: ldloc.1
IL_0020: call float64 [mscorlib]System.Math::Pow(float64, float64)
IL_0025: ldloc.1
IL_0026: ldc.r8 1.5707963267948966
IL_002f: mul
IL_0030: call float64 [mscorlib]System.Math::Sin(float64)
IL_0035: mul
IL_0036: stloc.2
IL_0037: ldarg.0
IL_0038: callvirt instance class [System.Windows.Forms]System.Windows.Forms.RichTextBox CETestGenerator.Form1::get_RichTextBox1()
IL_003d: ldloc.2
IL_003e: call int64 [mscorlib]System.BitConverter::DoubleToInt64Bits(float64)
IL_0043: stloc.3
IL_0044: ldloca.s V_3
IL_0046: call instance string [mscorlib]System.Int64::ToString()
IL_004b: callvirt instance void [System.Windows.Forms]System.Windows.Forms.RichTextBox::set_Text(string)
IL_0050: ret
}
如您所见,几乎相同。唯一的区别是两个附加的nop命令(不应该执行任何操作?)。
现在我的问题是,我是在做错什么,是编译器或框架在做怪异的事情,还是这就是它的原样? 我知道并不是每个数字都可以精确地用双精度表示。这就是为什么我比较Int64表示形式的原因。但是我的理解是,结果在两次构建之间不应该改变。
鉴于仅启用优化并不会改变它,那么Debug和Release之间的进一步区别是什么?
我正在为.NET Framework 4.5进行编译,并且如上所述,该错误仅发生在x86版本(或AnyCPU +首选32位选项)中。
编辑: 根据Tomers的评论,this question对待一个类似的想法,同时更加关注Debug / Release内部版本的差异。我仍然觉得有些奇怪,尽管当运行相同的代码时,不同的体系结构应该给出不同的结果。这是设计使然吗?我该如何信任自己计算出的值?