• 二进制运算误差问题


    我们知道在处理一些浮点数的时候,运算会产生误差,得到的是类似无限循环。

    例子:

      

        @Test
        public void Demo(){
            double a1=2.0;
            double a2=1.1;
            System.out.println(a2-a1);//-0.8999999999999999
        }

    其原因是计算机所使用二进制01代码无法准确表示某些带小数位的十进制数据。

    下面我们来分析下:
    我们知道将一个十进制数值转换为二进制数值,需要通过下面的计算方法:
    1. 整数部分:连续用该整数除以2,取余数,然后商再除以2,直到商等于0为止。然后把得到的各个余数按相反的顺序排列。简称"除2取余法"。
    2. 小数部分:十进制小数转换为二进制小数,采用"乘2取整,顺序排列"法。用2乘以十进制小数,将得到的整数部分取出,再用2乘余下的小数部分,然后再将积的整数部分取出,如此进行,直到积中的小数部分为0或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,即先取出的整数部分作为二进制小数的高位,后取出的整数部分作为低位有效位。简称"乘2取整法"。
    3. 含有小数的十进制数转换成二进制,整数、小数部分分别进行转换,然后相加。

    例如:将十进制数值25.75转换为二进制数值,步骤如下:

    25(整数部分)
    25/2=12......1
    12/2=6.......0
    6/2=3......0
    3/2=1......1
    1/2=0......1
    (25) 10=(11001) 2

    0.75(小数部分)
    0.75*2=1.5......1
    0.5*2=1......1
    (0.75) 10=(0.11) 2
    (25.75) 10=(11001) 2+(0.11) 2=(11001.11) 2

    按照上述方法,我们将0.65及0.6转换为二进制代码:
    (0.65)10 = (0.101001100110011001100110011001100110011......)2
    (0.6) 10 = (0.10011001100110011001100110011001100110011......)2

    后面的省略号表示已经算不完了,后面在无限重复 0011 这段二进制数值。

    如何解决这个问题?知道其根本原因后,我们知道是无法从根本上解决这个问题的,但我们可以有一些曲线救国的方法,下面列举几个:
    1. 因为二进制数值可以准确表示整数(可以使用整数转换为二进制方法验证下),所以可以将小数乘以10或100等变成整数,然后做整数运算,最后再通过除以10或100等获得结果;
    2. 通过截取结果的有效小数位数等,来取得最好的近似结果,然后在做处理。
    3. 对于可以用有限长度的二进制数值表示的十进制数值,可以使用存储位数大于其长度的数据类型。

    java中提供了BigDecimal这个超大的类,用来存储浮点型,使用它可以处理二进制误差问题。

    @Test
        public void Demo1(){
            BigDecimal b1=new BigDecimal(2.0+"");//注意:只能使用String类型的构造
            System.out.println(b1.subtract(new BigDecimal(1.1+"")));//0.9
        }

    其他具体方法查看api。大的整形数据使用BigDecimal

  • 相关阅读:
    企业级 SpringBoot 教程 (九)springboot整合Redis
    03 网格系统
    02 表单
    01 排版
    客户端调用webSerices
    sql 一行转多行
    sql 多行转一行
    时间差计算 Stopwatch
    sql 游标
    Linq连接查询
  • 原文地址:https://www.cnblogs.com/aigeileshei/p/5772390.html
Copyright © 2020-2023  润新知