gpt4 book ai didi

c# - 无关代码更改计算结果

转载 作者:行者123 更新时间:2023-12-04 11:42:20 26 4
gpt4 key购买 nike

我们有一些代码在某些机器上产生了意想不到的结果。我把它缩小到一个简单的例子。在下面的 linqpad 片段中,方法 GetValGetVal2具有基本相同的实现,尽管前者还包括对 NaN 的检查。但是,每个返回的结果是不同的(至少在我的机器上)。

void Main()
{
var x = Double.MinValue;
var y = Double.MaxValue;
var diff = y/10 - x/10;

Console.WriteLine(GetVal(x,6,diff));
Console.WriteLine(GetVal2(x,6,diff));
}

public static double GetVal(double start, int numSteps, double step)
{
var res = start + numSteps * step;
if (res == Double.NaN)
throw new InvalidOperationException();
return res;
}

public static double GetVal2(double start, int numSteps, double step)
{
return start + numSteps * step;
}

结果
3.59538626972463E+307
Infinity

为什么会发生这种情况,是否有一种简单的方法可以避免这种情况?跟寄存器有关系吗?

最佳答案

您没有指定环境和编译选项,但我已经能够在 中的 .NET Standard 4.8 中重现您的问题。 32 位 Release模式 输出在哪里

3,59538626972463E+307

32 位 Debug模式 结果是这样的
3,59538626972463E+307
3,59538626972463E+307
结果在 64 位模式下始终如下


这是因为在 32 位 .NET Standard 中,浮点运算是使用 80 位扩展浮点格式的 x87 指令完成的。 numSteps * step不适合 double并导致无穷大,但在扩展精度下它不会溢出并且最终结果适合 double .在计算表达式时,有时必须将中间值溢出到内存以释放一些寄存器,这会将值向下转换为 double精确。因此,相同的表达式可能会产生不同的结果。影响结果的另一件事是编译器优化:这里 GetVal2由编译器预先计算,编译器在 double 中执行所有操作精确。你可以很容易地看到编译器只是从内存中加载常量结果而不是调用 GetVal2在下面的拆解中§
这种现象在 64 位模式下不会发生,无论是 Debug 还是 Release 以及 .NET Standard 或 .NET Core,因为 64 位的数学运算总是在 SSE 寄存器中完成 .它也无法在 .NET Core 中重现,因为 .NET Core 也使用 SSE 寄存器来进行浮点数学运算,即使在 32 位中也是如此。您可以通过在 VS 中调试并查看反汇编来轻松检查。这是有道理的,因为 SSE 速度更快,结果更具确定性,而且过去 20 年中几乎所有现代计算机都支持 SSE
为了避免这种非确定性特性,最简单的方法是完全避免 32 位 x86,或者转移到 .NET Core,在那里确定性更有保证。否则你将不得不使用一些 3rd 方库。看
  • Deterministic floating point and .NET
  • Coercing floating-point to be deterministic in .NET?
  • Floating point determinism for gamedev in .NET Core
  • Is SSE floating-point arithmetic reproducible?
  • Is floating point arithmetic stable?
  • Is floating-point math consistent in C#? Can it be?
  • How deterministic is floating point inaccuracy?

  • 类似的问题还有很多:
  • C# - Inconsistent math operation result on 32-bit and 64-bit
  • Floating point inconsistency between expression and assigned object
  • CLR JIT optimizations violates causality?
  • Casting a result to float in method returning float changes result
  • Why does Math.Exp give different results between 32-bit and 64-bit, with same input, same hardware
  • Is SSE floating-point arithmetic reproducible?
  • Why does this floating-point calculation give different results on different machines?
  • C# XNA Visual Studio: Difference between "release" and "debug" modes?
  • C# rounding differently depending on platform?

  • FLT_EVAL_METHOD > 1 出现在 C 和 C++ 中时,这是相同的问题。 .
  • Does any floating point-intensive code produce bit-exact results in any x86-based architecture?
  • Is C floating-point non-deterministic?
  • Difference in floating point arithmetics between x86 and x64
  • different behaviour or sqrt when compiled with 64 or 32 bits
  • std::pow produce different result in 32 bit and 64 bit application
  • acos(double) gives different result on x64 and x32 Visual Studio

  • § 这是带有我的评论的完整反汇编列表。许多行消失了,因为它们已经被优化掉了
    命名空间 FloatDeterminism
    {
    类(class)计划
    {
    static void Main(string[] args)
    {
    var x = Double.MinValue;
    02C40848 55 推 ebp
    02C40849 8B EC mov ebp,esp
    02C4084B DD 05 90 08 C4 02 fld qword ptr [FloatDeterminism.Program.Main(System.String[])+048h (02C40890h)] #加载-1.7976931348623157e+308
    02C40851 83 EC 08 sub esp,8
    02C40854 DD 1C 24 fstp qword ptr [esp]
    02C40857 DD 05 98 08 C4 02 fld qword ptr [FloatDeterminism.Program.Main(System.String[])+050h (02C40898h)] # 加载 3.5953862697246315e+307
    02C4085D 83 EC 08 sub esp,8
    02C40860 DD 1C 24 fstp qword ptr [esp]
    02C40863 B9 06 00 00 00 mov ecx,6
    02C40868 FF 15 60 4D 1C 01 call dword ptr [Pointer to: FloatDeterminism.Program.GetVal(Double, Int32, Double) (011C4D60h)] # Call GetVal()
    02C4086E 83 EC 08 sub esp,8
    02C40871 DD 1C 24 fstp qword ptr [esp]
    02C40874 E8 7F 10 2F 70 调用 System.Console.WriteLine(Double) (72F318F8h) # Print GetVal()
    02C40879 DD 05 A0 08 C4 02 fld qword ptr [FloatDeterminism.Program.Main(System.String[])+058h (02C408A0h)] #加载inf
    02C4087F 83 EC 08 sub esp,8
    02C40882 DD 1C 24 fstp qword ptr [esp]
    02C40885 E8 6E 10 2F 70 调用 System.Console.WriteLine(Double) (72F318F8h) # Print GetVal2() = inf
    02C4088A 5D 流行 ebp
    02C4088B C3 ret
    Console.WriteLine(GetVal2(x, 6, diff));
    02C4088C 00 00 添加字节 ptr [eax],al
    02C4088E 00 00 添加字节 ptr [eax],al
    02C40890 FF ?? ??????
    02C40891 FF ?? ??????
    02C40892 FF ?? ??????
    02C40893 FF ?? ??????
    02C40894 FF ?? ??????
    02C40895 FF ?? ??????
    Console.WriteLine(GetVal2(x, 6, diff));
    02C40896 EF 输出 dx,eax
    02C40897 FF 99 99 99 99 99 调用 fword ptr [ecx-66666667h]
    02C4089D 99 cdq
    02C4089E C9离开
    02C4089F 7F 00 jg FloatDeterminism.Program.Main(System.String[])+059h (02C408A1h)
    02C408A1 00 00 添加字节 ptr [eax],al
    02C408A3 00 00 添加字节 ptr [eax],al
    02C408A5 00 F0 添加 al,dh
    02C408A7 7F 20 jg FloatDeterminism.Program.GetVal(Double, Int32, Double)+011h (02C408C9h)
    02C408A9 13 1C 01 adc ebx,dword ptr [ecx+eax]
    02C408AC 00 00 添加字节 ptr [eax],al
    02C408AE 00 00 添加字节 ptr [eax],al
    02C408B0 14 13 adc al,13h
    02C408B2 1C 01 sbb al,1
    02C408B4 58 pop eax
    02C408B5 4D 十二月
    02C408B6 1C 01 sbb al,1

    public static double GetVal(double start, int numSteps, double step)
    {
    var res = start + numSteps * step;
    02C408B8 56推esi
    02C408B9 50推eax
    02C408BA 89 0C 24 mov dword ptr [esp],ecx
    02C408BD DB 04 24 fild dword ptr [esp]
    02C408C0 DC 4C 24 0C fmul qword ptr [esp+0Ch]
    02C408C4 DC 44 24 14 fadd qword ptr [esp+14h]
    02C408C8 DD 05 00 09 C4 02 fld qword ptr [FloatDeterminism.Program.GetVal(Double, Int32, Double)+048h (02C40900h)]
    02C408CE DF F1 fcomip st,st(1) # 检查 NaN
    02C408D0 7A 06 jp FloatDeterminism.Program.GetVal(Double, Int32, Double)+020h (02C408D8h)
    02C408D2 75 04 jne FloatDeterminism.Program.GetVal(Double, Int32, Double)+020h (02C408D8h)
    02C408D4 DD D8 fstp st(0)
    02C408D6 EB 05 jmp FloatDeterminism.Program.GetVal(Double, Int32, Double)+025h (02C408DDh)
    02C408D8 59 流行 ecx
    02C408D9 5E pop esi
    02C408DA C2 10 00 ret 10h
    02C408DD B9 74 D6 3F 72 mov ecx,723FD674h
    02C408E2 E8 0D 28 57 FE 调用 CORINFO_HELP_NEWSFAST (011B30F4h)
    02C408E7 8B F0 mov esi,eax
    02C408E9 8B CE mov ecx,esi
    抛出新的 InvalidOperationException();
    02C408EB FF 15 AC D6 3F 72 call dword ptr [指向:System.InvalidOperationException..ctor() (723FD6ACh)]
    02C408F1 8B CE mov ecx,esi
    02C408F3 E8 88 2D 9D 71 调用 74613680
    02C408F8 CC int 3
    02C408F9 CC int 3

    关于c# - 无关代码更改计算结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46526896/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com