.net浮点舍入错误在编译时与运行时 [英] .net float rounding errors at compile-time vs runtime

查看:69
本文介绍了.net浮点舍入错误在编译时与运行时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近为测试用例设置了一些数据,以检查float数据类型上的舍入错误,并遇到了一些意外的结果。我希望案例t2和t3会产生与t1相同的结果,但是在我的机器上不是这种情况。谁能告诉我为什么?

I was recently setting up some data for a test case checking rounding errors on the float data type, and ran into some unexpected results. I expected that cases t2 and t3 would produce the same result as t1, but that is not the case on my machine. Can anyone tell me why?

我怀疑造成这种差异的原因是在编译时对t2和t3进行了评估,但令我惊讶的是编译器完全忽略了我的尝试强制其在评估期间使用中间浮点数据类型。 c#标准中是否有某些部分要求使用最大可用数据类型来评估常量,而不管指定了哪种常量?

I suspect the reason for the difference is that t2 and t3 are evaluated at compilation, but I'm surprised that the compiler completely ignores my attempts to force it to use an intermediate float data type during evaluation. Is there some part of the c# standard that mandates evaluating constants with the largest available data type, regardless of the one specified?

这是在win7 64位intel机器上运行.net 4.5.2。

This is on a win7 64-bit intel machine running .net 4.5.2.

  float temp_t1 = 1/(3.0f);
  double t1 = (double)temp_t1;

  const float temp_t2 = 1/(3.0f);
  double t2 = (double)temp_t2;

  double t3 = (double)(float)(1/(3.0f));

  System.Console.WriteLine( t1 ); //prints 0.333333343267441
  System.Console.WriteLine( t2 ); //prints 0.333333333333333
  System.Console.WriteLine( t3 ); //prints 0.333333333333333


推荐答案

人们经常对一致性有疑问浮点计算。在这一点上,.NET Framework几乎没有任何保证。 引用Eric Lippert

People often have questions about the consistency of floating point calculations. There are almost no guarantees given by the .NET Framework on this point. To quote Eric Lippert:


C#编译器,抖动和运行时都有宽广的纬度,可以随时为您提供比规范要求更准确的结果,一时兴起-不需要他们选择始终如一地这样做,实际上他们并不需要这样做。

The C# compiler, the jitter and the runtime all have broad lattitude to give you more accurate results than are required by the specification, at any time, at a whim -- they are not required to choose to do so consistently and in fact they do not.

在这种特殊情况下,答案很简单。发行版本的原始IL:

In this particular case, the answer is straight-forward. The raw IL for a release build:

IL_0000: ldc.r4 0.333333343
IL_0005: conv.r8
IL_0006: ldc.r8 0.33333333333333331
IL_000f: stloc.0
IL_0010: ldc.r8 0.33333333333333331
IL_0019: stloc.1
IL_001a: call void [mscorlib]System.Console::WriteLine(float64)
IL_001f: ldloc.0
IL_0020: call void [mscorlib]System.Console::WriteLine(float64)
IL_0025: ldloc.1
IL_0026: call void [mscorlib]System.Console::WriteLine(float64)
IL_002b: ret

全部这里的算术由编译器完成。在Roslyn编译器中,temp_t1是变量的事实导致编译器发出加载4字节浮点数的IL,然后将其转换为double。我相信这与以前的版本一致。在其他两种情况下,编译器以双精度执行所有算术并存储这些结果。毫不奇怪,第二种情况和第三种情况没有区别,因为编译器没有在IL中保留局部常量。

All arithmetic here is done by the compiler. In the Roslyn compiler, the fact that temp_t1 is a variable causes the compiler to emit IL that loads a 4-byte float and then convert it to a double. I believe this is consistent with previous versions. In the other two cases, the compiler does all arithmetic at double precision and stores those results. It is not surprising that the second and third cases don't differ because the compiler does not retain local constants in the IL.

这篇关于.net浮点舍入错误在编译时与运行时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆