javascript浮点运算

问题

console.log(0.1 + 0.2)
// 0.30000000000000004
  • 为什么不是0.3?这是由于十进制到二进制的转换,导致的精度问题。因为计算机执行的是二进制运算,当一个十进制数不能准确的转化成二进制数时,这种精度误差就无法避免。

    javascript浮点运算_第1张图片
    javascript浮点运算

  • 其实,在其他语言里面也存在浮点运算精度问题

javascript浮点运算_第2张图片
python浮点运算

引言

  • JavaScript的number类型按照ECMA的JavaScript标准,它的Number类型就是IEEE 754的双精度数值,相当于java的double类型。IEEE 754标准《二进制浮点数算法》(www.ieee.org)就是一个对实数进行计算机编码的标准。因此精度问题不止JS这门语言独有。

  • IEEE双精度格式具有53 位有效数字精度(包含符号号),并总共占用64 位。

  • 无论是用纸张记录数值,还是用计算机记录数值,都必须用某种编码方案来表达数值。必须理解的是,用编码表达的数值不是数值本身,而只是数值的一种人类或计算机可理解的描述。任何编码方案都有其局限,要么是表达范围(精度)方面的限制,要么是其他复杂性方面的制约。

  • 绝对完美的数值编码方案是不存在的,为了处理方便,这个标准引入了大量的折衷和妥协,建立在这种表达方式上的算法(例如除法运算)也一样。由于数值表达方式存在“缺陷”,运算结果不可避免地堆聚起越来越多的误差。

  • 按IEEE 754格式保存的浮点数精度相当于带有15、16或17位小数位数的十进制小数,由于存在二进制和十进制的转换问题,具体的位数会发生变化。要获得最高的转换精度,必须指定17位的小数——此时可以相信前15位的精度。

  • 在JavaScript中输出下面这些数值(注意不能作为字符串输出):0.1000000000000000000000000001(28位小数)、0.100000000000000000000000001(27位小数)、0.1000000000000000000000000456(28位小数)、0.09999999999999999999999(23位小数),显示出来的结果都是数值0.1。又如,如果输出1/3的有理数表达式,结果是0.3333333333333333。

  • 因此JavaScript小数在做四则运算时,精度会丢失。


验证

  • 需要具备二进制数转换为十进制数十进制小数转换为二进制小数的知识
 十进制0.1  
 => 二进制0.00011001100110011…(循环0011)   
 =>尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-4(二进制移码为00000000010),符号位为0  
 => 计算机存储为:0 00000000100 10011001100110011…11001  
 => 因为尾数最多52位,所以实际存储的值为0.00011001100110011001100110011001100110011001100110011001  
 而十进制0.2  
 => 二进制0.0011001100110011…(循环0011)  
 =>尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-3(二进制移码为00000000011),符号位为0  
 => 存储为:0 00000000011 10011001100110011…11001  
 因为尾数最多52位,所以实际存储的值为0.00110011001100110011001100110011001100110011001100110011  
 那么两者相加得:      
 0.00011001100110011001100110011001100110011001100110011001  
+  0.00110011001100110011001100110011001100110011001100110011 (确认??)  
 =  0.01001100110011001100110011001100110011001100110011001100  
 转换成10进制之后得到:0.30000000000000004   
console.log('0.00011001100110011001100110011001100110011001100110011001'.split('.')[1].length)
// 56
  • 从整个演算过程,我们的最初的疑问肯定迎刃而解了。因此这这并不能认为是js的bug,这几乎出现在很多的编程语言中:C/C++,Java中,准确的说:“使用了IEEE 754浮点数格式”来存储浮点类型(float 32,double 64)的任何编程语言都有这个问题!

参考

十进制小数转换为二进制小数
0.1+0.2!=0.3, why? how to solve? -- 简议javascript的浮点运算
彻底理解0.1 + 0.2 === 0.30000000000000004的背后


你可能感兴趣的:(javascript浮点运算)