why (1.0E+28f).ToString() == “9.999999E+27”?
本问题已经有最佳答案,请猛点这里访问。
我正在创建一个浮点控件,您可以在其中增加10、100、1000等。
我在控件中显示的文本是
当我增加一个数字时,我注意到:
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位左右。