在阅读此question和此msdn blog之后,我尝试了一些示例对此进行测试:
Console.WriteLine(0.8-0.7 == 0.1);
是的,预期输出为False
。因此,我尝试将双方的表达式都强制转换为double
和float
,以查看是否可以得到不同的结果:
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#编译器。
答案 0 :(得分:5)
在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.7
和0.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.7
和0.1
的结果分别转换为double
。由于它们已经是double
,因此没有任何效果,其结果与0.8-0.7 == 0.1
相同。
C# specification, version 5.0指示float
和double
是IEEE-754的32位和64位浮点类型。我没有看到它明确声明它们是二进制浮点格式而不是十进制格式,但是所描述的特征使这一点显而易见。该规范还指出,除以下例外情况外,通常使用IEEE-754算术,取整为最近(大概取整为最近的数到偶数)。
C#规范允许比标称类型更精确地执行浮点运算。第4.1.6节说:“…浮点运算的执行精度可能高于运算的结果类型……”这通常会使浮点表达式的分析复杂化,但在{{的情况下,与我们无关1}},因为唯一适用的运算是从0.8-0.7 == 0.1
中减去0.7
,并且这些数字在同一个binade中(在浮点表示中具有相同的2的幂),因此结果减法的精确度是可表示的,并且额外的精度不会改变结果。只要将源文本0.8
,0.8
和0.7
转换为0.1
不会使用额外的精度,并且转换为double
会产生{ {1}}(没有额外的精度),结果将如上所述。 (C#标准在第6.2.1节中指出,从float
到float
的转换会产生一个double
值,尽管它没有明确指出此时不得使用任何额外的精度。 )
在float
中,float
的数字为8,8-0.7 == 7.3
的数字为8
,7.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.31
,18.01
,对于0.6999999999999999555910790149937383830547332763671875
是0.7
,对于17.309999999999998721023075631819665431976318359375
为17.31
,则结果为假。
如果两者都被浮点数相减,那么如何从减去18.01中减去8个差?
18.01大于8,并且在其浮点表示中需要更大的2的幂。同样,17.31000000000000227373675443232059478759765625
的结果大于18.01-0.7
的结果。这意味着有效位中的位(浮点表示的小数部分,由2的幂进行缩放)表示更大的值,从而导致浮点运算中的舍入误差通常更大。通常,浮点格式具有固定的跨度-从保留的高位到保留的低位有固定的距离。当您更改为左侧有更多位(高位)的数字时,右侧有一些位(低位)被推出,结果将更改。