浮点数加减丢失精度原因剖析

这里以1.005为例子,模拟二进制存储

1为整数位

 

package baseJava;

import java.math.BigDecimal;
import java.util.HashMap;

public class DoubleTest {
	
	public static void main(String[] args) {
		double decimalPart = 0.005;
		String binaryStr = getBinaryStr(decimalPart);
		System.out.println(binaryStr);
	}
	
	private static String getBinaryStr(double a) {
		BigDecimal aa = new BigDecimal(a+"");
		
		String returnStr = "";
		HashMap map = new HashMap<>();
		for(;aa.compareTo(BigDecimal.ZERO)!=0;) {
			aa = aa.multiply(new BigDecimal("2"));
			System.out.println(aa);
			if(map.containsKey(aa)) {//key 相同说明已经开始循环
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			map.put(aa, "");
			if(aa.compareTo(BigDecimal.ONE)>=0) {
				returnStr = returnStr +"1";
				aa = aa.subtract(BigDecimal.ONE);
			}else {
				returnStr = returnStr +"0";
			}
			System.out.println(returnStr);
		}
		return returnStr;
	}
}

1、使用map存储已经使用过的值,假如break的话,就代表开始循环了。

结果如下

0.010
0
0.020
00
0.040
000
0.080
0000
0.160
00000
0.320
000000
0.640
0000000
1.280
00000001
0.560
000000010
1.120
0000000101
0.240
00000001010
0.480
000000010100
0.960
0000000101000
1.920
00000001010001
1.840
000000010100011
1.680
0000000101000111
1.360
00000001010001111
0.720
000000010100011110
1.440
0000000101000111101
0.880
00000001010001111010
1.760
000000010100011110101
1.520
0000000101000111101011
1.040
00000001010001111010111
0.080
000000010100011110101110

0.160
......
0.080
00000001010001111010111000010100011110101110
0.160
000000010100011110101110000101000111101011100
0.320
0000000101000111101011100001010001111010111000
0.640
00000001010001111010111000010100011110101110000
1.280
000000010100011110101110000101000111101011100001
0.560
0000000101000111101011100001010001111010111000010
1.120
00000001010001111010111000010100011110101110000101
0.240
000000010100011110101110000101000111101011100001010
0.480
0000000101000111101011100001010001111010111000010100
0.960
00000001010001111010111000010100011110101110000101000
1.920
000000010100011110101110000101000111101011100001010001
1.840
0000000101000111101011100001010001111010111000010100011
1.680
00000001010001111010111000010100011110101110000101000111
1.360
000000010100011110101110000101000111101011100001010001111
0.720
0000000101000111101011100001010001111010111000010100011110
1.440
00000001010001111010111000010100011110101110000101000111101
0.880
000000010100011110101110000101000111101011100001010001111010
。。。。。。

 

很明显,1.005的二进制表示方式是以“00001010001111010111”一直不断循环的

计算机存储double的空间是64位

doubleç±»åæ°æ®çå­å¨æ¹å¼

1是符号位

2-12是阶位

13-64是小数点后面的尾数

也就是计算机只存了

1.005在内存的存储是如下:

符号位 1
阶位 0
尾数(52位) 0000000101000111101011100001010001111010111000010100

这时候精度会丢失。

将结果反算为十进制,看看丢失多少精度

公式:(0.10)2 = 1*(1/2)^1 + 0*(1/2)^2  

	// (0.10)2 = 1*(1/2)^1 + 0*(1/2)^2 0.0049999999999998934185896359849721193313598632812500
	private static BigDecimal getDdecimalStr(String binaryStr) {
		char [] arr = binaryStr.toCharArray();
		BigDecimal sum = BigDecimal.ZERO ;
		for(int i=0;i

结果为0.0049999999999998934185896359849721193313598632812500

这也就解释了为什么用double计算会有精度丢失的问题。

 

2、BigDecimal 计算为什么不丢失精度?

请看下篇文章

为什么BigDecimal 计算不丢进度?

 

 

 

你可能感兴趣的:(Java,JDK)