发新话题
打印

Java浮点数为什么精度会丢失

Java浮点数为什么精度会丢失

由于对float或double 的运用不当, 能够会呈现精度丧失的效果。 效果大概情况可以通过如下代码了解:
  Java代码
  public class FloatDoubleTest {
  public static void main(String[] args) {
  float f = 20014999;
  double d = f;
  double d2 = 20014999;
  System. out. println("f=" + f);
  System. out. println("d=" + d);
  System. out. println("d2=" + d2);
  }
  }
  得到的后果如下:
  f=2. 0015E7
  d=2. 0015E7
  d2=2. 0014999E7
  从输出后果可以看出double 可以正确的表示20014999 , 而float 没有方法表示20014999 , 得到的只是一个近似值。 这样的后果很让人讶异。 20014999 这么小的数字在float下没方法表示。 带着这个效果, 一起学习一下浮点数, 做个简单分享, 希望有助于大家对java 浮点数的了解。
  1. 关于 java 的 float 和 double 的表示法
  Java 语言支持两种根本的浮点类型: float 和 double 。 java 的浮点类型都根据 IEEE 754 标准。 IEEE 754 定义了32 位和 64 位双精度两种浮点二进制小数标准。
  IEEE 754 用迷信记数法以底数为 2 的小数来表示浮点数。
  对于32 位浮点数float用 第1 位表示数字的符号, 用第2至9位来表示指数, 用 最初23 位来表示尾数, 即小数部分。
  float(32位):
  对于64 位双精度浮点数, 用 第1 位表示数字的符号, 用 11 位表示指数, 52 位表示尾数。
  double(64位):
  都是分为三个部分:
  (1) 一个独自的符号位s 直接编码符号s 。
  (2)k 位的幂指数E , 移码表示 。
  (3)n 位的小数, 原码表示 。
  2.  什么时分会呈现无法表示?
  任何一个数字, 在java底层表示都必须转换成这种迷信计数法来表示, 那么我们来想想看什么时分这个数字会无法表示呢?那么只要两种情形:
  1. 幂数不够表示了:这种情况往往呈现在数字太大了, 超越幂数所能承受的范围, 那么这个数字就无法表示了。 如幂数最大只能是10, 但是这个数字用迷信计数法表示时, 幂数一定会超越10, 就没方法了。
  2. 尾数不够表示了:这种情况往往呈现在数字精度太长了, 如1. 3434343233332这样的数字, 虽然很小??共怀??, 这种情况下幂数完全满足要求, 但是尾数已经不能表示出来了这么长的精度。
  3.   20014999 为什么用 float 没有方法正确表示?
  通过以上剖析, 应该已经知道, 这个数字不大, 转换成IEEE754迷信计数法之后幂数一定是满足要求的, 只是尾数不能表示这么精确的数字了。
  结合 float和double的表示方法, 通过剖析 20014999 的二进制表示就可以知道答案了。
  以下程序可以得出 20014999 在 double 和 float 下的二进制表示方式。
  Java代码
  public class FloatDoubleTest3 {
  public static void main(String[] args) {
  double d = 20014999;
  long l = Double. doubleToLongBits(d);
  System. out. println(Long. toBinaryString(l));
  float f = 20014999;
  int i = Float. floatToIntBits(f);
  System. out. println(Integer. toBinaryString(i));
  }
  }
  输出后果如下:
  Double:100000101110011000101100111100101110000000000000000000000000000
  Float:1001011100110001011001111001100
  对于输出后果剖析如下。 对于都不 double 的二进制右边补上符号位 0 刚好可以得到 64 位的二进制数。 根据double的表示法, 分为符号数、幂指数和尾数三个部分如下:
  0 10000010111 0011000101100111100101110000000000000000000000000000
  对于 float 右边补上符号位 0 刚好可以得到 32 位的二进制数。  根据float的表示法,  也分为 符号数、幂指数和尾数三个部分如下 :
  0 10010111 00110001011001111001100
  绿色部分是符号位, 红色部分是幂指数, 蓝色部分是尾数。
  对比可以得出:
  符号位都是 0 。
  幂指数为移码表示, 两者刚好也相等。
  独一不同的是尾数。
  在 double 的尾数为: 001100010110011110010111 0000000000000000000000000000 , 省略后面的零, 至少需求24位才干正确表示 。
  而在 float 下面尾数为: 00110001011001111001100 , 共 23 位。
  为什么会这样?原因很明显, 因为 float 尾数 最多只能表示 23 位, 所以 24 位的 001100010110011110010111 在 float 下面经过四舍五入变成了 23 位的 00110001011001111001100 。 所以 20014999 在 float 下面变成了 20015000 。
  也就是说 20014999 虽然是在float的表示范围之内, 但 在 IEEE 754 的 float 表示法精度长度没有方法表示出 20014999 , 而只能通过四舍五入得到一个近似值。
  小结
  浮点运算很少是精确的, 只需是超越精度能表示的范围就会产生误差。 往往产生误差不是因为数的大小???且蛭??木?取?因此, 产生的后果接近但不等于想要的后果。 尤其在运用 float 和 double 作精确运算的时分要特别小心。
  可以考虑采用一些替代方案来实现。 如通过 String 结合 BigDecimal 或者通过运用 long 类型来转换。文章由 yusutag.tk 御淑堂丰胸产品 整理,收集辛苦,希望能保留出处。
jiyizhen-buy.com颈椎保健枕太空记忆枕 igerl.com歌瑞尔内衣

TOP

发新话题