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

这里以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<BigDecimal,String> 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位

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<arr.length;i++) {
            BigDecimal a = new BigDecimal(binaryStr.substring(i, i+1));
            BigDecimal temp= a.multiply(new BigDecimal("0.5").pow(i+1)) ;
            sum = sum.add(temp)  ;
        }
        return sum;
    }
结果为0.0049999999999998934185896359849721193313598632812500

这也就解释了为什么用double计算会有精度丢失的问题。
————————————————
版权声明:本文为CSDN博主「家鸽996」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yangxujia/article/details/82766130

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值