双精度浮点数和浮点数精度之间的差异

时间:2019-06-25 08:37:18

标签: c# casting floating-point precision

在阅读此question和此msdn blog之后,我尝试了一些示例对此进行测试:

Console.WriteLine(0.8-0.7 == 0.1);

是的,预期输出为False。因此,我尝试将双方的表达式都强制转换为doublefloat,以查看是否可以得到不同的结果:

Console.WriteLine((float)(0.8-0.7) == (float)(0.1));
Console.WriteLine((double)(0.8-0.7) == (double)(0.1));

第一行输出True但第二行输出False,为什么会发生这种情况?

此外,

Console.WriteLine(8-0.7 == 7.3);
Console.WriteLine(8.0-0.7 == 7.3);

以上两行即使没有强制转换也给出True。还有...

Console.WriteLine(18.01-0.7 == 17.31);

此行输出False。如果将两者都用浮点数相减,则如何从减法18.01中减去8的差?

我尝试通读博客和问题,但似乎找不到其他答案。有人可以向我解释为什么所有这些都是以莱曼的语言发生的吗?先感谢您。

编辑:

Console.WriteLine(8.001-0.001 == 8); //this return false
Console.WriteLine(8.01-0.01 == 8); //this return true

注意:我正在使用.NET fiddle在线c#编译器。

1 个答案:

答案 0 :(得分:5)

0.8−0.7的情况

0.8-0.7 == 0.1中,没有一个文字可以在double中精确地表示。最接近的可表示值是0.8的0.8000000000000000444089209850062616169452667236328125、0.7的0.6999999999999999555910790149937383830547332763671875和0.1的0.1000000000000000055511151231257827021181583404541015625。当减去前两个时,结果为0.100000000000000088817841941970012523233890533447265625。由于这不等于第三个,因此0.8-0.7 == 0.1的计算结果为false。

(float)(0.8-0.7) == (float)(0.1)中,0.8-0.70.1的结果分别转换为float。最接近前者的float值0.1000000000000000055511151231257827021181583404541015625为0.100000001490116119384765625。最接近后者的float值0.100000000000000088817841970012523233890533447265625为0.100000001490116119384765625。由于这些相同,因此(float)(0.8-0.7) == (float)(0.1)的计算结果为true。

(double)(0.8-0.7) == (double)(0.1)中,0.8-0.70.1的结果分别转换为double。由于它们已经是double,因此没有任何效果,其结果与0.8-0.7 == 0.1相同。

注释

C# specification, version 5.0指示floatdouble是IEEE-754的32位和64位浮点类型。我没有看到它明确声明它们是二进制浮点格式而不是十进制格式,但是所描述的特征使这一点显而易见。该规范还指出,除以下例外情况外,通常使用IEEE-754算术,取整为最近(大概取整为最近的数到偶数)。

C#规范允许比标称类型更精确地执行浮点运算。第4.1.6节说:“…浮点运算的执行精度可能高于运算的结果类型……”这通常会使浮点表达式的分析复杂化,但在{{的情况下,与我们无关1}},因为唯一适用的运算是从0.8-0.7 == 0.1中减去0.7,并且这些数字在同一个binade中(在浮点表示中具有相同的2的幂),因此结果减法的精确度是可表示的,并且额外的精度不会改变结果。只要将源文本0.80.80.7转换为0.1不会使用额外的精度,并且转换为double会产生{ {1}}(没有额外的精度),结果将如上所述。 (C#标准在第6.2.1节中指出,从floatfloat的转换会产生一个double值,尽管它没有明确指出此时不得使用任何额外的精度。 )

其他案件

float中,float的数字为8,8-0.7 == 7.3的数字为87.29999999999999982236431605997495353221893310546875的数字为0.6999999999999999555910790149937383830547332763671875,而7.3的数字为7.29999999999999982282231631605997495353221893310546875结果是正确的。

请注意,C#规范允许的附加精度可能会影响0.7的结果。对于这种情况,使用额外精度的C#实现在这种情况下可能会产生false,因为对于8-0.7会得到不同的结果。

8-0.7中,对于8-0.7,我们有18.010000000000001563194018672220408916473388671875对于18.01-0.7 == 17.3118.01,对于0.69999999999999995559107901499373838305473327636718750.7,对于17.30999999999999872102307563181966543197631835937517.31,则结果为假。

  

如果两者都被浮点数相减,那么如何从减去18.01中减去8个差?

18.01大于8,并且在其浮点表示中需要更大的2的幂。同样,17.31000000000000227373675443232059478759765625的结果大于18.01-0.7的结果。这意味着有效位中的位(浮点表示的小数部分,由2的幂进行缩放)表示更大的值,从而导致浮点运算中的舍入误差通常更大。通常,浮点格式具有固定的跨度-从保留的高位到保留的低位有固定的距离。当您更改为左侧有更多位(高位)的数字时,右侧有一些位(低位)被推出,结果将更改。