关于c#:why(1.0E + 28f).ToString()==“9.999999E + 27”?

why (1.0E+28f).ToString() == “9.999999E+27”?

本问题已经有最佳答案,请猛点这里访问。

我正在创建一个浮点控件,您可以在其中增加10、100、1000等。

我在控件中显示的文本是myFloatNumber.ToString("E");

当我增加一个数字时,我注意到:

1
2
3
4
5
6
7
8
9
1.0E+1f  =  1.0E+1f
1.0E+2f  =  1.0E+2f
1.0E+3f  =  1.0E+3f
......
......
1.0E+25f  =  1.0E+25f
1.0E+26f  =  1.0E+26f
1.0E+27f  =  1.0E+27f
1.0E+28f  =  9.999999E+27f   // Why!!?????

这个问题的解决办法是用双份的,但我很好奇为什么这种行为?

代码如下:

1
2
3
        string str1 = (1.0E27f).ToString();  // str1 ="1E+27"
        string str2 = (1.0E28f).ToString();  // str2 ="9.999999E+27";
        string str3 = (1.0E29f).ToString();  // str3 ="1E+29"


您必须考虑到1.0e28f是十进制(以10为基数)表示。将此字符串(源代码中的文字或数据文件中的值)转换为浮点表示转换为基2(浮点内部表示)。

10^28附近的3个最近的浮点值正好是

1
2
3
0.9999998261528069050908803072e28f
0.9999999442119689768320106496e28f <- the nearest one
1.0000000622711310485731409920e28f

或在基底2中:

1
2
3
1.00000010011111100111000 * 2^93
1.00000010011111100111001 * 2^93
1.00000010011111100111010 * 2^93

选择最近的一个(中间),即内存中的浮点,而不是1.0e28。

当您请求toString()时,您将把0.99999944221969768320106496e28f转换回十进制形式。听起来,默认格式是在浮点分隔符之前打印1个小数,在浮点之后打印6个小数(很像c print f%f格式)

精确到0.999999944211968768320106496E28F的十进制数实际上是9.9999999E27,相当直接。

编辑

请注意,5^3接近2^7,因为单精度浮点有24位有效位,24*3/7约为10,所以单精度浮点中精确表示的5的最大功率为5^10。

因此,在浮点中精确表示的10的最大功率是1.0e10。从11点到27点,每一种力量都像你所期望的那样,幸运的是。好吧,不完全靠运气,但因为舍入误差小于0.5*10^(n-7)。

编辑2-关于Double

double并不能真正"解决"问题,因为有了53位的有效位,你就可以达到53*3/7,即10^22,而不会出现舍入误差。23及以上的每10次方可四舍五入。但是,如果你一直打印6位小数,它就解决了,因为四舍五入发生在15位左右。