问题描述
我最近为测试用例设置了一些数据,以检查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几乎没有任何保证。 :
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:
在这种特殊情况下,答案很简单。发行版本的原始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浮点舍入错误在编译时与运行时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!