【问题标题】:difference of float/double conversion betwen VS2012 and VS2015VS2012 和 VS2015 浮点/双精度转换的区别
【发布时间】:2017-08-05 23:20:42
【问题描述】:

我有以下代码:

 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 拆解:

           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 拆解:

            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 中的错误吗?或者这种行为是否由某些已更改的特定设置控制? 谢谢!

【问题讨论】:

  • 我的第一个猜测是,roslyn 有不同的构建参数和/或不同的 .net 版本会导致这种情况
  • 这是调试还是发布? 64位构建的结果是什么? 32 位构建使用 x87,因此它不像 SSE2 那样快速和一致
  • 程序集列表相同,但地址不同。数据段选择器 ds:[...] 指向编译时常量(显然 0.02f * 28f 的结果被烘焙到程序集中)。运行时没有乘法。
  • @dlatikay 2012 版本有 dword 指令,而其他地方只有 qword 指令(我不知道它是否相关)
  • doubles 总是给出不同的结果——它是一个二进制浮点数。它会尝试尽可能接近正确的数字,但并非总是如此。它还取决于在后台为其分配了多少内存。

标签: c# visual-studio visual-studio-2012 visual-studio-2015


【解决方案1】:

这里的问题是,在 Roslyn 编译器中,在编译时执行的常量浮点计算与早期版本略有不同 - 这导致了不同的行为。

但是,由于 C# 标准的这一部分,这不是错误:

4.1.6 浮点类型

浮点运算可以以比运算结果类型更高的精度执行。例如,一些硬件架构支持比 double 类型具有更大范围和精度的“扩展”或“long double”浮点类型,并使用这种更高精度类型隐式执行所有浮点运算。只有在性能成本过高的情况下,才能使此类硬件架构以较低的精度执行浮点运算,而不是要求实现同时丧失性能和精度,C# 允许将更高精度的类型用于所有浮点运算.除了提供更精确的结果之外,这很少有任何可衡量的影响。然而,在 x * y / z 形式的表达式中,乘法产生的结果超出了双精度范围,但随后的除法将临时结果带回双精度范围,事实上,表达式在更高的范围内计算范围格式可能会导致产生有限结果而不是无穷大。要强制浮点类型的值精确到其类型的精度,可以使用显式转换。

发生的情况是,您看到了上述导致的未定义行为的结果,其中 Roslyn 编译器在编译时完成的浮点计算使用的精度与早期编译器在编译时完成的计算不同。

请注意,Roslyn 编译器的初始版本中实际上存在一个错误,该错误已由更新 2 修复:https://github.com/dotnet/roslyn/issues/7262 - 但这只是一个兴趣点,与 VS2012 和 VS2012 之间的结果差异没有直接关系VS2015(带有更新 2)。

有关详细信息,另请参阅以下内容:

【讨论】:

  • 谢谢!关于错误报告,这是否意味着 VS2012 被窃听?还是 2015 年?还是没有?
  • @lezebulon VS2015 编译器的第一个版本具有在更新 2 中修复的错误,但您看到的结果差异与此无关,不是错误。我已经澄清了我的答案以使这一点更清楚。
  • 在我看来,这两种情况下的算术运算都是在编译时完成的(这是我所期望的)——它只是在 Roslyn 中以稍微不同的方式完成。您提到您认为 double 值的算术 没有 在编译时完成......这不是我所看到的 - 你能更详细地说吗?
  • @Jon 其实你是对的 - 我会相应地修改答案。 (我的答案基于我链接的页面中来自gafter 的 cmets - 我假设他是权威来源!)。
  • @JonSkeet 我关于“not double”的评论是为了表明如果您使用double 值和类型而不是float,这些差异似乎不会发生。我删除了评论,因为它是多余的(并且令人困惑)。
猜你喜欢
  • 2023-03-18
  • 2010-10-22
  • 1970-01-01
  • 2011-01-24
  • 2010-10-11
  • 2015-06-12
相关资源
最近更新 更多