Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 编译时与运行时的.net浮点舍入错误_C#_.net_Casting_Type Conversion - Fatal编程技术网

C# 编译时与运行时的.net浮点舍入错误

C# 编译时与运行时的.net浮点舍入错误,c#,.net,casting,type-conversion,C#,.net,Casting,Type Conversion,我最近为一个测试用例设置了一些数据,用于检查浮点数据类型的舍入错误,并遇到了一些意外的结果。我希望案例t2和t3会产生与t1相同的结果,但在我的机器上并非如此。谁能告诉我为什么 我怀疑造成这种差异的原因是t2和t3在编译时进行计算,但令我惊讶的是,编译器完全忽略了我在计算期间强制它使用中间浮点数据类型的尝试。c#标准中是否有一部分要求使用最大的可用数据类型来计算常量,而不管指定了哪种类型 这在运行.net 4.5.2的win7 64位intel计算机上 float temp_t1 = 1/(

我最近为一个测试用例设置了一些数据,用于检查浮点数据类型的舍入错误,并遇到了一些意外的结果。我希望案例t2和t3会产生与t1相同的结果,但在我的机器上并非如此。谁能告诉我为什么

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

这在运行.net 4.5.2的win7 64位intel计算机上

  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
C#浮点行为基于底层CPU,使用IEEE 754格式。如果你真的想知道发生了什么,你需要看看二进制格式的数字,把它们转换成字节。当您打印它们时,它们会从基数2转换为基数10,并且需要进行大量处理

以下是我怀疑正在发生的事情。第一次计算(temp_t1)使用单精度浮点,尾数为23位。我怀疑,但没有确认,temp_t2和t2是由编译器中的优化组件转换的,因此temp_t2不是使用单精度浮点计算的,而是使用双精度计算的,t2获取了该值


有关浮点行为的更多信息:

人们经常对浮点计算的一致性提出疑问。在这一点上,.NET Framework几乎没有提供任何保证:

C#编译器、抖动和运行时都有广泛的用途,可以在任何时候、一时兴起的情况下为您提供比规范所要求的更精确的结果——它们不需要选择一致的方式,事实上它们也不需要

在这种特殊情况下,答案是直截了当的。发布版本的原始IL:

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是一个变量这一事实导致编译器发出IL,该IL加载一个4字节浮点,然后将其转换为双精度浮点。我相信这与以前的版本是一致的。在另外两种情况下,编译器以双精度执行所有运算并存储这些结果。第二种和第三种情况没有区别并不奇怪,因为编译器在IL中没有保留局部常量。

我建议您阅读。我不认为需要使用最大的可用数据类型。编译器、运行时和/或抖动在任何时候都允许使用比要求更高的精度。谢谢迈克,这正是我想要的。如果你愿意把它写下来作为一个答案,我会接受的。对于编译器作者来说,典型的“我做了该死的,我不做该死的”问题。您可以使用常量进行算术运算,例如常量浮点因子=0.1f;const tweak=因子*Math.Pi;世界上没有一个程序员认为tweak会损失8位数的精度是合理的。老实说,我不觉得编译器/抖动/运行时比我要求的更精确。除了测试数据之外,我想不出有任何情况会有意引入舍入误差。如果我希望使用一些特定的值进行测试,那么使用它可能更好。