【计算机基础】 浮点数的使用小结

浮点数的存储原理

//————float类型计算机存储原理
单精度(32位)浮点数的结构: 名称 长度
比特位置 符号位 Sign (S) : 1bit (b31) 指数部分Exponent (E)
8bit (b30-b23) 尾数部分Mantissa (M) : 23bit (b22-b0)
其中的指数部分(E)采用的偏置码(biased)的形式来表示正负指数,若E<127则为负的指数,否则为非负的指数。
另外尾数部分M存储的是当把一个浮点数规范化表示后的1.zozooz…(二进制的)形式的zozooz的部分的比特串,共23位. 求值方法:
(-1)^S*(1.M)*2^(E-127)
///————double类型计算机存储原理
双精度(64位)浮点数的结构与单精度相仿 名称 长度
比特位置 符号位 Sign (S) : 1bit (b63) 指数部分Exponent (E)
11bit (b62-b52) 尾数部分Mantissa (M) : 52bit (b51-b0)
双精度的指数部分(E)采用的偏置码为1023
求值方法:(-1)^S*(1.M)*2^(E-1023)//E的最大值是2047————最小值是0
/

举个例子
如1.0的存储结构是0x3ff0000000000000,M部分是0,E=1023
(-1)^0 1.0 2^(1023-1023) = 1.0
1.25的存储结构是0x3ff4000000000000,M部分是2^(-2),E=1023
(-1)^0 1.25 2^(1023-1023) = 1.25
从double的小数位我们可以看到,他小数部分能表示的范围0——1-2^(-52)。。小数点后面有十进制16位,其中第16位不可靠的。取最大值的时候52个bit位全都是1
2.220446049250313E-16

  1. 浮点数不合适精确计算
    double类型不适合精确计算,记住 我们计算机只能尽最大努力来表示一个浮点数,他并不能完整的真实的表示一个10进制小数。。。在计算机中一个十进制小数=∑Fi*2^(-i),其中[1<=i<=52],Fi取值0或1。当然我们计算机会尽最大的努力表示一个浮点数。如以下例子,具体为什么,下面会有解释。
    0.99999999999999999(17个9),计算机中会用1.0来代替他
    1.00000000000000012计算机中会用1.0000000000000002来代替他

  2. 二进制的精度,10进制小数转换成2进制小数丢失了多少数据。。单个误差范围 正负2^(-52)
    计算机中的纯小数:如果一个小数*2^N能等于某个整数的时候,他在计算机里面就能够无损的存储,当然N的大小是有限制的,这个和double/float的具体表示有关。。。。否则任何一个非整数,在计算机中存储都是很有可能会丢失部分精度的,比如double类型他的精度是2^(-52);近似于2.220446049250313E-16这也是计算机中小数点后面之有16位的一个原因,其中第16位是不可信的。。
    通常十进制中的小数转换成二进制小数都是会损失精度的,当然这个损失非常小。。。一般一个误差在正负2^(-52)以内,就是说一个double类型的十进制数字,在计算机内部表示时与他的真实误差在正负2^(-52)以内,(计算机内部二进制存储的时候也是采取四舍五入的,不过这个四舍五入和我们十进制的四舍五入还是稍微有点区别的, 我们来说明一下,小数中计算机不能表示的部分超过2^(-53),就进一位(计算机中就是52个比特位中最右边那个位进一),即加上一个2^(-52),这样的数字就比原生数字大了,小数中计算机不能表示的部分小于2^(-53)(近似00000000000000011),就舍掉他们。。。

  3. ————————-示例————————-
    我们看到下面几个数在计算机中的存储都是一样的
    1)、
    System.out.println(1.00000000000000022);;
    System.out.println(1.00000000000000033);;
    System.out.println(1.00000000000000012);;
    他们在计算机中的表示11111111110000000000000000000000000000000000000000000000000001,
    所以计算机会认为他们是相等的,都等于1.0000000000000002近似1+2^(-52), 小数点后有16位,只有15位是信的。在我们看来这三个数不一样,但是计算机的识别能力有限,在他看来这三个数都是1.0000000000000002。。。
    1.00000000000000033比1+2^(-52)+2^(-53)小, 所以他在计算机中就用1+2^(-52)来表示,剩下多的部分就被舍掉了。
    1.00000000000000012比1+2^(-26)大但是比,所以就进一位变成了1+2^(-52)————其他很多情况下,只要你用浮点数表示时,他就不是一个准确值了,,特别是当很多浮点数同时进行运算时,一个个不可靠的值计算在一起,他们的结果就偏离正确的值越来越远了,偏高偏低都有可能,特别是当有乘法时这种误差会被放大很多倍。
    2)、(1.03-0.42)==0.61的结果是false

  4. 对于足够大的整形数据——————
    同样的道理,当用科学计数法来表示一个足够的数字时,最大能保存的无损数字是在十进制16位左右,因为科学计数的小数位最多只有16位,而且第十六位还是不可靠的,所以超过16位时,比如2^(62)你用long来保存和网络传输都有可能会导致精度丢失,,(以前我曾经在数据库中用long类型作主键,而这个long类型的数值最后到了19位,在和其他系统交换数据的时候就出现问题了)

  5. 总结
    综上所述,浮点数(除了某些纯计算机小数)和一些超大整数字,计算机直接存储起来都是会丢失数据的,虽然丢失的数据对整体而言误差很小但是累加在一起就足以可以影响计算结果了,而且必然会影响比较判断(<=>)的结果。计算的时候如果需要精确计算的话,都不可以直接用double来计算。如果有必要的话,处理时可以采用BigDecimal,网络传输时可以采用String类型来替换等等。

—–附
Java中的一些方法,将double类型的计算机存储形式输出来
long rawLongBits= Double.doubleToRawLongBits(value);
Long.toBinaryString(rawLongBits)
Long.toHexString(rawLongBits)

你可能感兴趣的:(计算机基础)