BidDecimal对于精确性的问题,以及如何保持输入数据的准确性

BidDecimal对于精确性的问题,以及如何保持输入数据的准确性

引子

对于这个问题,咱们先引入一个案例,以下:javascript

题目要求

有如下double数组:
double[] arr = {0.1, 0.2, 0.33, 4.4, 5.55, 0.006};
请编程计算它们的总值及平均值(四舍五入保留小数点后2位)java

开始编写时候的错误

没有对数组中的数据进行字符串的转化,致使了计算结果的不精确,以下:web

double[] arr = {0.1, 0.2, 0.33, 4.4, 5.55, 0.006};
        BigDecimal total = new BigDecimal(0);
        for (int i = 0; i < arr.length; i++) {
            BigDecimal bd = new BigDecimal(arr[i]);
            total = total.add(bd);
        }
        System.out.println("数组中的各个元素和为:" + total);

        BigDecimal average = total.divide(BigDecimal.valueOf(arr.length), 2, BigDecimal.ROUND_HALF_UP);
        System.out.println("它们的平均数为:" + average);
//输出结果
数组中的各个元素和为:10.58600000000000020995705174442491625086404383182525634765625
它们的平均数为:1.76

改进

后来通过改进,对bd的格式进行了限定编程

double[] arr = {0.1, 0.2, 0.33, 4.4, 5.55, 0.006};
        BigDecimal total = new BigDecimal("0");
        for (int i = 0; i < arr.length; i++) {
            BigDecimal bd = new BigDecimal(String.valueOf(arr[i]));
            total = total.add(bd);
        }
        System.out.println("数组中的各个元素和为:" + total);

        BigDecimal average = total.divide(BigDecimal.valueOf(arr.length),2,BigDecimal.ROUND_HALF_UP);
        System.out.println("它们的平均数为:"+average);
//输出结果
数组中的各个元素和为:10.586
它们的平均数为:1.76

分析

BigDecimal bd1 = new BigDecimal(Double.valueOf(0.1));
        System.out.println("bd1是" + bd1);
        BigDecimal bd2 = new BigDecimal(0.1f);
        System.out.println("bd2是" + bd2);
        BigDecimal bd3 = new BigDecimal(0.1);
        System.out.println("bd3是" + bd3);
        BigDecimal bd4 = new BigDecimal("0.1");
        System.out.println("bd4是" + bd4);
        System.out.println("0.1是" + 0.1);
        System.out.println("Float.valueOf(0.1f)" + Float.valueOf(0.1f));
//输出结果
bd1是0.1000000000000000055511151231257827021181583404541015625
bd2是0.100000001490116119384765625
bd3是0.1000000000000000055511151231257827021181583404541015625
bd4是0.1
0.10.1
Float.valueOf(0.1f)0.1

从这里你们应该已经能看出来了,所谓的更精确的BigDecimal其实与咱们理解意义上的精确是不同的,有人会问了,既而后三个(BigDecimal(“0.1”);0.1;0.1f)结果都是咱们常识中认为的0.1,那咱们直接用0.1不就行了,干吗非要用BigDecimal呢?
在讲0.1到底是什么东西以前,我先再给你们写一段小玩意儿:数组

System.out.println(0.1);
        System.out.println(0.2);
        System.out.println(0.1 + 0.2);
        System.out.println(0.3);
        double a = 0.1;
        double b = 0.2;
        System.out.println(a + b);
//输出结果
0.1
0.2
0.30000000000000004
0.3
0.30000000000000004

其实,0.1做为咱们熟知的一个小数,它没法准确地表示为 double(或者说对于该状况,不能表示为任何有限长度的二进制小数).这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值).那为何咱们sout的语句输出的0.1(会默认输入的为Double)依旧直接打印为0.1,而不是像上面三个,带了一串尾巴呢?我为你们追踪一下sout的源码ide

System.out.println(0.1);
public void println(double x) {
        synchronized(this) {
            this.print(x);
            this.newLine();
        }
    }
public void print(double d) {
        this.write(String.valueOf(d));
    }

明白了吧,你打印的,只是0.1这么个被转成字符串的东西,而不是计算机中所认为的0.1Double,也就是上面所说的那句话,0.1没法准确地表示为 double.svg

有人又会提出质疑了,那这个也不能用,你提的BigDecimal精确却是精确,那也是一串尾巴,那我用啥?
你们别忘了我上面写过的bd4啊,我再把上面那段代码拿下来:this

BigDecimal bd1 = new BigDecimal(Double.valueOf(0.1));
        System.out.println("bd1是" + bd1);
        BigDecimal bd2 = new BigDecimal(0.1f);
        System.out.println("bd2是" + bd2);
        BigDecimal bd3 = new BigDecimal(0.1);
        System.out.println("bd3是" + bd3);
        BigDecimal bd4 = new BigDecimal("0.1");
        System.out.println("bd4是" + bd4);
        System.out.println("0.1是" + 0.1);
        System.out.println("Float.valueOf(0.1f)" + Float.valueOf(0.1f));
//输出结果
bd1是0.1000000000000000055511151231257827021181583404541015625
bd2是0.100000001490116119384765625
bd3是0.1000000000000000055511151231257827021181583404541015625
bd4是0.1
0.10.1
Float.valueOf(0.1f)0.1

是否是?bd4的输出结果是否是也是0.1?什么?你说它是字符串?那你接着看spa

//输出结果
        BigDecimal bd1 = new BigDecimal(0.1);
        BigDecimal bd2 = new BigDecimal(0.2);
        BigDecimal bd3 = new BigDecimal("0.1");
        BigDecimal bd4 = new BigDecimal("0.2");
        BigDecimal sum1 = bd1.add(bd2);
        BigDecimal sum2 = bd3.add(bd4);
        System.out.println("bd1是" + bd1);
        System.out.println("bd2是" + bd2);
        System.out.println("bd3是" + bd3);
        System.out.println("bd4是" + bd4);
        System.out.println("sum1是" + sum1);
        System.out.println("sum2是" + sum2);
//输出结果
bd1是0.1000000000000000055511151231257827021181583404541015625
bd2是0.200000000000000011102230246251565404236316680908203125
bd3是0.1
bd4是0.2
sum1是0.3000000000000000166533453693773481063544750213623046875
sum2是0.3

神奇吧?也就是说,全部在BigDecimal中的运算,你尽管加上双引号,那样的结果就是咱们所熟知的结果.不管在输入 计算 仍是打印的过程当中,都会展示给咱们0.1 + 0.2 = 0.3(依旧提醒你们不要字符串拼接±*/,不然你懂得)code

结论

数学运算,调用BigDecimal中的运算方法,数字加上双引号,变成字符串便可