gpt4 book ai didi

.net - CLR JIT 优化违反了因果关系?

转载 作者:行者123 更新时间:2023-12-03 21:17:24 27 4
gpt4 key购买 nike

我正在为一位同事写一个有启发性的例子,向他展示为什么测试 float 是否相等通常是一个坏主意。我使用的示例是将 0.1 添加十次,并与 1.0(我在介绍性数字类(class)中展示的那个)进行比较。我惊讶地发现两个结果是相等的( code + output )。

float @float = 0.0f;
for(int @int = 0; @int < 10; @int += 1)
{
@float += 0.1f;
}
Console.WriteLine(@float == 1.0f);

一些调查表明不能依赖这个结果(很像 float 平等)。我发现最令人惊讶的是添加代码 其他代码可能会更改计算结果 ( code + output )。请注意,此示例具有与 IL 完全相同的代码,只是附加了一行 C#。
float @float = 0.0f;
for(int @int = 0; @int < 10; @int += 1)
{
@float += 0.1f;
}
Console.WriteLine(@float == 1.0f);
Console.WriteLine(@float.ToString("G9"));

我知道我不应该在 float 上使用相等性,因此不应该太在意这个,但我发现它非常令人惊讶,就像我向所有人展示的一样。执行计算后执行的操作会更改前一计算的值吗?我认为这不是人们通常想到的计算模型。

我并没有完全难倒,假设在“相等”情况下发生了某种优化会改变计算结果(在 Debug模式下构建可以防止“相等”情况),这似乎是安全的。显然,当 CLR 发现以后需要对浮点数进行装箱时,优化就被放弃了。

我已经搜索了一下,但找不到这种行为的原因。谁能给我点线索?

最佳答案

这是 JIT 优化器工作方式的副作用。如果要生成的代码更少,它会做更多的工作。原始代码段中的循环被编译为:

                @float += 0.1f;
0000000f fld dword ptr ds:[0025156Ch] ; push(intermediate), st0 = 0.1
00000015 faddp st(1),st ; st0 = st0 + st1
for (int @int = 0; @int < 10; @int += 1) {
00000017 inc eax
00000018 cmp eax,0Ah
0000001b jl 0000000F

当您添加额外的 Console.WriteLine() 语句时,它会将其编译为:
                @float += 0.1f;
00000011 fld dword ptr ds:[00961594h] ; st0 = 0.1
00000017 fadd dword ptr [ebp-8] ; st0 = st0 + @float
0000001a fstp dword ptr [ebp-8] ; @float = st0
for (int @int = 0; @int < 10; @int += 1) {
0000001d inc eax
0000001e cmp eax,0Ah
00000021 jl 00000011

注意地址 15 与地址 17+1a 的区别,第一个循环将中间结果保留在 FPU 中。第二个循环将其存储回@float 局部变量。当它留在 FPU 内时,结果以全精度计算。然而,将它存储回来会将中间结果截断回浮点数,从而在此过程中损失大量精度。

虽然令人不快,但我不认为这是一个错误。 x64 JIT 编译器的行为有所不同。您可以在 connect.microsoft.com 提出您的案例

关于.net - CLR JIT 优化违反了因果关系?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2225503/

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