我有以下代码:
float a = 0.02f * 28f;
double b = (double)a;
double c = (double)(0.02f * 28f);
Console.WriteLine(String.Format(" {0:F20}", b));
Console.WriteLine(String.Format(" {0:F20}", c));
然而,无论是从VS2012还是VS2015(均具有“标准”设置)编译,它都会返回不同的结果
在VS2012中
0,56000000238418600000
0,55999998748302500000
在VS2015中:
0,56000000238418600000
0,56000000238418600000
VS2012 dissasembly:
float a = 0.02f * 28f;
0000003a mov dword ptr [ebp-40h],3F0F5C29h
double b = (double)a;
00000041 fld dword ptr [ebp-40h]
00000044 fstp qword ptr [ebp-48h]
double c = (double)(0.02f * 28f);
00000047 fld qword ptr ds:[001D34D0h]
0000004d fstp qword ptr [ebp-50h]
VS2015 dissasembly:
float a = 0.02f * 28f;
001E2DE2 mov dword ptr [ebp-40h],3F0F5C29h
double b = (double)a;
001E2DE9 fld dword ptr [ebp-40h]
001E2DEC fstp qword ptr [ebp-48h]
double c = (double)(0.02f * 28f);
001E2DEF fld dword ptr ds:[1E2E7Ch]
001E2DF5 fstp qword ptr [ebp-50h]
我们可以看到两种情况下的反汇编都不相同,这是正常的吗?这可能是VS2012或VS2015中的错误吗?或者这种行为是由一些特定的设置控制的? 谢谢!
答案 0 :(得分:5)
这里的问题是,在Roslyn编译器中,在编译时完成的常量浮点计算与早期版本的执行方式略有不同 - 这会导致不同的行为。
但是,由于C#标准的这一部分,这不是一个错误:
4.1.6浮点类型
可以以比操作的结果类型更高的精度执行浮点运算。例如,某些硬件体系结构支持“扩展”或“长双”浮点类型,其范围和精度比double类型更大,并使用此更高精度类型隐式执行所有浮点运算。只有在性能成本过高的情况下,才能使这种硬件架构以较低的精度执行浮点运算,而不是要求实现失去性能和精度,C#允许更高精度的类型用于所有浮点运算。除了提供更精确的结果外,这几乎没有任何可衡量的影响。但是,在x * y / z形式的表达式中,乘法产生的结果超出双范围,但随后的除法将临时结果带回双范围,表达式的计算结果更高范围格式可能导致产生有限结果而不是无穷大。要将浮点类型的值强制为其类型的精确精度,可以使用显式强制转换。
发生的事情是您正在看到由上述结果导致的未定义行为的结果,其中Roslyn编译器在编译时完成的浮点计算使用与编译时完成的计算不同的精度早期的编译器。
请注意,Roslyn编译器的初始版本中确实存在一个由Update 2修复的错误:https://github.com/dotnet/roslyn/issues/7262 - 但这只是一个感兴趣的点,与结果之间的差异没有直接关系VS2012和VS2015(更新2)。
有关详细信息,请参阅以下内容: