浮点戏剧性错误(小数部分完全丢失)

Floating point dramatic error (fractional part is completely lost)

quotient = 43156414f / 3;

我在这里得到 quotient == 14385472 (完全不是真实值应该是:14385471,333...).它完全失去了所有小数部分!

我知道浮点计算不准确(似乎不是我应该知道的全部),但是,正如我告知的那样,错误可能出现在更远的有效数字处。但是这里的红利只有8位数字。为什么会出现如此严重的错误?

可选子问题:我应该牢记哪些规则以预见将来出现此类错误?

请注意:将除法类型从 float 更改为 double 可解决此问题。

float 的精度为 6-9 位。您的价值太大,无法在没有损失的情况下放入浮点数。

double 的精度约为 15-17 位。

作为示例,检查 (int)43156414f(double)43156414f 的值 - 它们都是 43156416

嗯,float (Single) 使用 23 位作为尾数

https://en.wikipedia.org/wiki/Single-precision_floating-point_format

因此 float 可以表示最大 2**24 - 1 == 16777215 的整数,即 接近 14385471。 我们的可能性是:

 01001011 01011011 10000001 00111111 correponds to 14385471f (let's add 1 bit)
 01001011 01011011 10000001 01000000 correponds to 14385472f

所以我们可以看到我们只有 14385471f14385472f 可供选择;在 floate 的情况下,14385471.33333f 没有位置。 一起来看看

 float x = 14385471.3333333333f;

 byte[] data = BitConverter.GetBytes(x);

 Console.Write(" ", string.Join(" ", data
   .Reverse()
   .Select(b => Convert.ToString(b, 2).PadLeft(8, '0'))));

我们会有

 01001011 01011011 10000001 00111111

对应14385471f

我们还有更多问题
 quotient = 43156414f / 3;

现在,43156414f > 2**24 - 1 (16777215)43156414f 不能表示为 float

 01001100 00100100 10100000 11110000 corresponds to 43156416 

所以quotient的实际值为

 43156416 / 3 == 14385472      

最后,如果你想要14385471.33Single不够,试试double:

 // 14385471.333333334
 double quotient = 43156414d / 3;