c# - double float 和 float 精度之间的差异

标签 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#编译器。

最佳答案

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节说:“ ...浮点运算的执行精度可能高于运算的结果类型……”这通常会使浮点表达式的分析复杂化,但是在,因为唯一适用的运算是从0.8-0.7 == 0.1减去0.7,并且这些数字在同一个binade中(在浮点表示中具有相同的2的幂),因此相减的结果可精确表示并且更高的精度不会改变结果。只要将源文本0.80.80.7转换为0.1时不使用额外的精度,并且将转换为double的结果生成的float也没有额外的精度,结果将是如上所述。 (C#标准在第6.2.1节中说,从floatdouble的转换会产生一个float值,尽管它没有明确指出此时不能使用任何额外的精度。)

其他案例

float中,对于8-0.7 == 7.3为8,对于87.29999999999999982236431605997495353221893310546875,对于7.3为0.6999999999999999555910790149937383830547332763671875,对于0.7为7.29999999999999982236431605997495353221893310546875,因此结果为true。

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

8-0.7中,对于18.01-0.7 == 17.31我们有18.010000000000001563194018672220408916473388671875,对于18.010.6999999999999999555910790149937383830547332763671875,对于0.717.309999999999998721023075631819665431976318359375,对于17.3117.31000000000000227373675443232059478759765625,因此结果为假。


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


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

关于c# - double float 和 float 精度之间的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56749949/

相关文章:

c# - 是否可以将变量转换为存储在另一个变量中的类型?

c# - .Net 多个应用程序实例但单个文档

C# 随机浮点闭区间

c - 由于 SIGFPE,算术异常,除法运算导致错误执行

c++11 - std::numeric_limits::quiet_NaN() 与 std::nan() 与 NAN

c# - Master-Details View 中的 RenderTargetBitmap GDI 句柄泄漏

c# - 无法将类型隐式转换为“System.Collections.Generic.List”

c++ - 将 int32 转换为 uint32 是无操作吗?

c++ - 有没有可靠的方法在 double 中传递微秒时间?

java - 如何设置列表的引用类型?