在一些数字代码中,我注意到在x86或AnyCPU +"Prefer 32-bit"下编译的调试和发布版本给出了不同的结果。我已经将我的代码分解到几乎是重现问题的最小值。结果是,在一个计算步骤中只有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中,调试版本给出4498558851738655340
,发布版本给出4498558851738655341
(注意最后一位数字)。
我尝试在调试构建中手动启用优化(同样地,DEBUG常量和pdb生成),但结果保持不变。只有将完整构建类型更改为Release才能更改结果。
更进一步,我尝试比较IL,如Telerik的JustDecompile所给出的:
调试版本:
.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 + Prefer 32位选项)中。
- 编辑:**根据Tomers的评论,this question处理了类似的想法,同时更关注调试/发布版本的差异。我仍然觉得有点奇怪,不同的架构在运行相同的代码时会给出不同的结果。这是设计好的吗?那么我如何信任我计算的值呢?
1条答案
按热度按时间aiazj4mn1#
在
Windows 11
上,我已经在Visual Studio 2019
上使用.Net Framework 4.7.2
和在Visual Studio 2022
上使用.Net 6
测试了您的代码(做了一些修改)。使用
Debug
或Release
版本显示的结果总是相同的。我看不出任何差异。Visual Studio 2019
始终返回以40结尾的值作为调试版本,Visual Studio 2022
在为x64
cible编译时返回以41结尾的值。Visual Studio 2022
安装在我的PC上是一个64位的应用程序。我使用了以下程序
x86
生成正在显示以下结果x64
生成正在显示以下结果x86
和x64
构建版本之间的差异是由于Math.Sin()
功能(参见变量x4和x5)!