1 бит в двойном значении изменяется между сборкой x86 Debug и Release - PullRequest
0 голосов
/ 16 октября 2018

В некотором числовом коде я заметил, что сборка отладки и выпуска при компиляции для x86 или с AnyCPU + «Prefer 32-bit» давала разные результаты.Я разбил свой код до минимума, который воспроизводит проблему.Оказывается, что только один бит изменяется на одном из этапов вычисления.

Код:

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 приводит к изменению результата.

Идя дальше, я попытался сравнить IL, как указано в JustDecompile от Telerik:

Отладочная сборка:

.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-bit option).

Редактировать: ВВ свете комментариев Томера, этот вопрос рассматривает аналогичную идею, в то же время уделяя больше внимания различиям в сборках Debug / Release.Я все еще нахожу немного странным, что разные архитектуры должны давать разные результаты, хотя при запуске одного и того же кода.Это по замыслу?Как я могу доверять значениям, которые я тогда вычисляю?

...