一串数字什么鬼?java中float类型计算不精确

一、写java小程序的时候碰到的奇怪事儿

今天同学让帮忙给她解决一个数学迭代问题,题目很简单,写代码用个循环就可以计算出结果。核心的java代码最开始如下:

float priceBefore=1.0f;   float priceNow = 1.0f;
for(int i = 1;i<10000;i++){
            priceNow = priceNow + 0.02f;
            System.out.println("第"+i+"天,大米的价格是:"+priceNow+"  要大于"+priceBefore*1.05+"才可以卖的哦");
            if(priceNow>priceBefore*1.05){
                System.out.println("第"+i+"天,大米可以卖啦");
                priceBefore = priceNow;
                price.add(priceNow);
                priceTime.add(i);
                int canSell = (int) (sell*0.05);
                sell = sell - canSell;
                sum = sum+canSell;
                if(sum*priceNow>10000){
                    System.out.println("第"+i+"天,大米价格是:"+priceNow+",可卖大米"+sum+"斤,赚:"+sum*priceNow+"元钱!");
                    break;
                }
            }   

我们看到代码的输出是:

第1天,大米的价格是:1.02 要大于1.05才可以卖的哦
第2天,大米的价格是:1.04 要大于1.05才可以卖的哦
第3天,大米的价格是:1.06 要大于1.05才可以卖的哦
第3天,大米可以卖啦
第3天,可卖大米500
第4天,大米的价格是:1.0799999 要大于1.1129999399185182才可以卖的哦
第5天,大米的价格是:1.0999999 要大于1.1129999399185182才可以卖的哦
第6天,大米的价格是:1.1199999 要大于1.1129999399185182才可以卖的哦
第6天,大米可以卖啦
...

我们看到奇怪的到循环第四次的时候,出现了1.0799999这样一连串数字,按照代码逻辑这时参数的结果应该是1.08才对!这对于我这个强迫症患者来说绝对不能容忍,虽说差别只是0.0000001,但是总感觉不是很准确。


二、这是float类型计算不精确搞的鬼

《Effective Java》中已经讲出了这种问题,float/double不能停供完全精确的计算结果。这个原理其实很简单,float/int都是32bit(也就是一共有2^32个精确值),而int的范围是-2^31 ~ 2^31-1,而Float的最大值是3.4028235e+38,远大于2^31 - 1。而且,int只负责个数有限的整数,而浮点却要用来表示个数无穷的小数,显然力不从心。浮点精确值可以简单视作一个以0为中心的正态分布,绝对值越小(越接近0的地方),相邻两个精确值月密集。比如,最近的两个值可能只相差0.00000...几十个0...01,而最远的两个精确值,却差了2.028241E31。


三、如何解决float类型计算不精确的问题

已经封装好的工具包java.math.BigDecimal可以帮助我们完美地解决float/double类型计算不精确的问题。与浮点不同,它可以提供精度任意(当然在硬件限制范围内)的计算结果,但是,只能进行四则运算或者基于四则运算的其他简单运算。
重新修改我的代码:

 float priceBefore=1.0f;    float priceNow = 1.0f;
 for(int i = 1;i<10000;i++){
            BigDecimal temp = new BigDecimal(Float.toString(priceNow));
            BigDecimal add = new BigDecimal(Float.toString(0.02f));
            priceNow=temp.add(add).floatValue();
            BigDecimal priceBeforeTemp = new BigDecimal(Float.toString(priceBefore));
            BigDecimal multi = new BigDecimal(Float.toString(1.05f));
            float priceThrold = priceBeforeTemp.multiply(multi).floatValue();
            System.out.println("第"+i+"天,大米的价格是:"+priceNow+"  要大于"+priceThrold+"才可以卖的哦");
            if(priceNow>priceThrold){
                System.out.println("第"+i+"天,大米可以卖啦");
                priceBefore = priceNow;
                price.add(priceNow);
                priceTime.add(i);
                int canSell = (int) (sell*0.05);
                sell = sell - canSell;
                sum = sum+canSell;
                if(sum*priceNow>10000){
                    System.out.println("第"+i+"天,大米价格是:"+priceNow+",可卖大米"+sum+"斤,赚:"+sum*priceNow+"元钱!");
                    break;
                }
            }           

我们看到关键代码行

BigDecimal temp = new BigDecimal(Float.toString(priceNow));
BigDecimal add = new BigDecimal(Float.toString(0.02f));
priceNow=temp.add(add).floatValue();

以及:

BigDecimal priceBeforeTemp = new BigDecimal(Float.toString(priceBefore));
BigDecimal multi = new BigDecimal(Float.toString(1.05f));
float priceThrold = priceBeforeTemp.multiply(multi).floatValue();

就是利用BigDecimal对float类型数据分别进行加法以及乘法运算。
这时候我们再来看输出:

第1天,大米的价格是:1.02 要大于1.05才可以卖的哦
第2天,大米的价格是:1.04 要大于1.05才可以卖的哦
第3天,大米的价格是:1.06 要大于1.05才可以卖的哦
第3天,大米可以卖啦
第4天,大米的价格是:1.08 要大于1.113才可以卖的哦
第5天,大米的价格是:1.1 要大于1.113才可以卖的哦
第6天,大米的价格是:1.12 要大于1.113才可以卖的哦
第6天,大米可以卖啦

完美!

你可能感兴趣的:(一串数字什么鬼?java中float类型计算不精确)