title: JAVA浮点计算经验
date: 2016-08-4
- JS
- JAVA
- float
当浮点数进行运算的时候由于十进制与二进制相互转换的问题,可能出现不等于想要的结果
1当浮点数进行运算的时候由于十进制与二进制相互转换的问题,可能出现不等于想要的结果
例如
2.2 + 2.1 = 4.300000000000001
0.1 + 0.2 = 0.30000000000000004
首先是十进制与二进制转换的问题:
小数转换为二进制
要点:乘二取整,正序排列
解释:对被转换的小数乘以2,取其整数部分(0或1)作为二进制小数部分,取其小数部分,再乘以2,又取其整数部分作为二进制小数部分,然后取小数部分,再乘以2,直到小数部分为0或者已经去到了足够位数。每次取的整数部分,按先后次序排列,就构成了二进制小数的序列
例如把0.2转换为二进制,转换过程如图:
10-2-0.2.gif
0.2乘以2,取整后小数部分再乘以2,运算4次后得到的整数部分依次为0、0、1、1,结果又变成了0.2,
若果0.2再乘以2后会循环刚开始的4次运算,所以0.2转换二进制后将是0011的循环,即:0.0011 0011 0011 0011…
2 JS 数字丢失精度的原因
js采用双精度存储(double precision),占用 64 bit。
(1) 1位用来表示符号位
(2) 11位用来表示指数
(3) 52位表示尾数
浮点数转换成十进制存在无限循环的情况,例如
0.1 >> 0.0001 1001 1001 1001…(1001无限循环)
0.2 >> 0.0011 0011 0011 0011…(0011无限循环)
此时只能模仿十进制进行四舍五入了,但是二进制只有0和1两个,于是变为0舍1入。
这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是52位,因此JS中能精准表示的最大整数是Math.pow(2, 53),
十进制即 9007199254740992。
大于 9007199254740992 的可能会丢失精度
9007199254740992 >> 10000000000000...000 // 共计 53 个 0
3 解决方案
对于整数,前端出现问题的几率几乎没有(除非特别大超出精度范围),
只要运算结果不超过Math.pow(2, 53) 就不会丢失精度。
对于小数,前端出现问题的几率还是很多的,尤其在一些电商网站涉及到金额等数据。
解决方式:
把小数放到位整数(乘10倍数),再缩小回原来倍数(除10倍数)
例如计算
0.1 + 0.2
应写为
(0.1*10 + 0.2*10) / 10 == 0.3