double转换为int以及浮点型相加损失精度问题

最近在作支付相关模块的业务,数据库字段却使用的是double类型,其实也行,只要计算不在sql语句中进行,也是没有问题的。java

预先的类属性设置的是Double类型,本身算的时候发现小数相加会出现损失精度的状况程序员

以下情形sql

 @Test public void testDouble(){ Double [] arr = {39.9d,50d,198d,39.9d}; Double verify = 0d; for (Double aDouble : arr) { verify += aDouble ; } System.out.println(verify); }

输出的结果是:327.79999999999995  理应为:328数据库

待着疑惑试了下js编程

一看是一个效果,精度都会缺失。百度了下,解释以下编程语言

产生缘由:
    计算机并不能识别除了二进制数据之外的任何数据,不管咱们使用何种编程语言,在何种编译环境下工做,都要先把源程序翻译成二进制的机器码后才能被计算机识别。
    而在存储浮点型数据时,会分为三部分进行存储:
  1.         符号位(Sign): 0表明正,1表明为负
  2.         指数位(Exponent):用于存储科学计数法中的指数数据,而且采用移位存储
  3.         尾数部分(Mantissa):尾数部分
    其中前两位分别为实数符号位和指数符号位,因而存储结构实际为以下:

 

                    31                  30            29----23        22----0         

               实数符号位    指数符号位    指数位      有效数位测试

 

       可是,在将十进制浮点数转换为二进制浮点数时,小数的二进制有时也是不可能精确的,就如同十进制不能准确表示1/3,二进制也没法准确表示1/10,而double类型存储尾数部分最多只能存储52位,因而,计算机在存储该浮点型数据时,便出现了精度丢失。spa

  例如,11.9的内存存储大约为:1011. 1110011001100110011001100...翻译

       而在进行浮点类数据计算的时候,浮点参与计算,会左移或右移n位,直到小数点移动到第一个有效数字的右边。因而11.9在转化为二进制后 小数点左移3位,就获得1. 011 11100110011001100110(精度丢失2)code

       因而最终浮点型运算出现了精度丢失偏差。

 解决方式,java中可使用 BigDecimal 来解决

 @Test public void test3(){ System.out.println(0.11+2001299.32);//非精确的输出
        BigDecimal bigDecimal1 = new BigDecimal(Double.toString(0.11)); BigDecimal bigDecimal2 = new BigDecimal(Double.toString(2001299.32)); System.out.println(bigDecimal1.add(bigDecimal2));//精确的输出
    }

这种方式能够解决,而且很完美,可是还有一种方式比较容易些,毕竟是金额,小数位只有两位,能够先将其扩大100倍,再进行计算,计算完毕以后再除100,也能够解决(这么不要脸的方式也只有我这么low的程序员使用了),惭愧,出错了。

在计算的过程总遇到double转int的状况,总结了下实现的方式

    Double d = 1.7d;

    @Test
    public void test1(){
        // 这样会报错,由于double转换为字符串以后有.
        System.out.println(Integer.parseInt(d.toString()));
    }

    @Test
    public void test2_1(){
        // double 类型能够直接转为int类型
        double dd = 1.1;
        int ddd = (int)dd;
        System.out.println(ddd);
    }

    @Test
    public void test2_2(){
        // 能够经过强转进行转换,Double是包装类,不能直接进行强转,能够拆箱以后再次强转。
        int aa = (int)(Double.parseDouble(d.toString()));
        System.out.println(aa);
    }

    @Test
    public void test3(){
        // 这种方式最为简单
        System.out.println(d.intValue());
    }

    @Test
    public void test4(){
        DecimalFormat format1 = new DecimalFormat("#");
        String s = format1.format(d);
        System.out.println(s);
    }

    @Test
    public void test5(){
        DecimalFormat format1 = new DecimalFormat("#.#");
        String s = format1.format(1.35);
        System.out.println(s);
    }

    @Test
    public void test6(){
        DecimalFormat format1 = new DecimalFormat("0.000");
        String s = format1.format(1.35);
        System.out.println(s);
    }

 

DecimalFormat 要四舍五入须要加上 setRoundingMode(RoundingMode.HALF_UP); 网上是这么说的,可是本身测试默认就会四舍五入

 

特殊字符说明
“0” 指定位置不存在数字则显示为0 123.123 ->0000.0000 ->0123.1230
“#” 指定位置不存在数字则不显示 123.123 -> ####.#### ->123.123
“.” 小数点
“%” 会将结果数字乘以100 后面再加上% 123.123 ->#.00->1.3212%

 

 

 嗯,就这么个坑了。

相关文章
相关标签/搜索