Java中关于float与double类型的思考

479 阅读2分钟

最近遇到一个计算总金额的需求,我的做法是把double类型的金额相加,以为任务完成了,就提交代码了。代码合并的时候被告知这么写是错的,因此我研究了一下。

以float为例,先来看下面这段代码,并猜测一下结果

        float c = 1.0f;
        float b = 0.9f;
        System.out.println(c - b);
        System.out.println(c + b);

我们知道,数值在计算机中是以二进制的形式存储的,数值的计算先转成二进制,得出结果后再转成十进制。float第1位是符号位,2-9位是指数位,剩下23位是尾数位,这里取后24位,那么1.0的存储就是:10000 0000 0000 0000 0000 0000,0.9存储的是0111 0011 0011 0011 0011 0011,因此1-0.9和1+0.9的过程分别是

        10000 0000 0000 0000 0000 0000
    -   0111  0011 0011 0011 0011 0011 
        ______________________________
    =   0000 1100 1100 1100 1100 1101
    
         10000 0000 0000 0000 0000 0000
    +    0111  0011 0011 0011 0011 0011 
        ______________________________
    =    1111 0011 0011 0011 0011 0011

使用Math.pow方法在编译器中计算结果,并与原来的值相比较:

0.100000024
1.9
0.10000002384185791
1.8999999761581421

分析:尾数部分最大是1-2的负23次方,2的23次方是8 388 608,因此float精度是在7-8位左右,超出部分四舍五入,这里以9位为例,那么相减取的9位为0.100000024,相加取得九位为1.900000000,当小数位为0时,会舍弃除第一个小数位之外的0,所得到的结果即为1.9

结论:如果两个浮点数运算得到的结果是你想要的,那只是凑巧。在涉及金额运算时,不要用浮点数类型直接计算