第二章 Java浮点数精确计算

1、实际意义

在实际开发中,如果需要进行float或double的精确计算(尤其是财务计算),直接使用float或double是不行的(具体的例子看下边的代码的main方法的测试结果),需要使用BigDecimal。

2、代码

package com.xxx.util;

import java.math.BigDecimal;

/**
 * 浮点数精准算法
 */
public class BigDecimalArithUtil {

    private static final int DIV_SCALE = 10;//除法精度(除不尽时保留10为小数)

    /** 小数精确加法  */
    public static double add(double d1,double d2)
    {
        BigDecimal bd1 = BigDecimal.valueOf(d1);
        BigDecimal bd2 = BigDecimal.valueOf(d2);
        return bd1.add(bd2).doubleValue();
    }

    /** 小数精确减法  */
    public static double sub(double d1,double d2)
    {
        BigDecimal bd1 = BigDecimal.valueOf(d1);
        BigDecimal bd2 = BigDecimal.valueOf(d2);
        return bd1.subtract(bd2).doubleValue();
    }

    /** 小数精确乘法  */
    public static double mul(double d1,double d2)
    {
        BigDecimal bd1 = BigDecimal.valueOf(d1);
        BigDecimal bd2 = BigDecimal.valueOf(d2);
        return bd1.multiply(bd2).doubleValue();
    }

    /** 小数精确除法   */
    public static double div(double d1,double d2)
    {
        BigDecimal bd1 = BigDecimal.valueOf(d1);
        BigDecimal bd2 = BigDecimal.valueOf(d2);
        /*
         * 当除不尽时,以四舍五入的方式(关于除不尽后的值的处理方式有很多种)保留小数点后10位小数
         */
        return bd1.divide(bd2, DIV_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static void main(String[] args)
    {
        //测试加法
        System.out.println("0.05+0.01="+BigDecimalArithUtil.add(0.05,0.01));
        System.out.println("0.05+0.01="+(0.05+0.01));
        //测试减法
        System.out.println("1.0-0.42="+BigDecimalArithUtil.sub(1.0,0.42));
        System.out.println("1.0-0.42="+(1.0-0.42));
        //测试乘法
        System.out.println("4.015*100="+BigDecimalArithUtil.mul(4.015,100));
        System.out.println("4.015*100="+(4.015*100));
        //测试除法
        System.out.println("123.3/100="+BigDecimalArithUtil.div(123.3,100));
        System.out.println("123.3/100="+(123.3/100));
    }

}

3、注意点

  • 上边的程序我用的是BigDecimal.valueOf(double x)来将double型的x封装成BigDecimal,查看源码如下:

        /**
         * 注意:这通常是将double和float转换为一个BigDecimal的最好方式
         */
        public static BigDecimal valueOf(double val) {
            return new BigDecimal(Double.toString(val));
        }

    在这里直接先将double转换为了String,然后使用如下构造方法再将String转换为BigDecimal

     public BigDecimal(String val)

    这是最好的做法。如果使用的是直接将double或float转换为BigDecimal的方式,也就是说使用的如下构造器,那么将可能得不到精确结果。(看注释)

        /**
         * 注意:The results of this constructor can be somewhat unpredictable.
         * 该构造器的结果有时是不准确的
         */
        public BigDecimal(double val)

  • 在实际使用中还可能使用int和long来进行浮点数的精确计算(具体做法:将浮点数先乘以相应的倍数转化为int(<=9位十进制数)或long(<=18位十进制数),计算之后再除以之前的倍数,得出结果),而且该种方式的性能会更高,具体的int、long和BigDecimal各自的使用看《Effective Java(第二版)》第48条。
  • 需要指出的是,在实际开发中,BigDecimal的性能差的问题基本可以忽略,是浮点数精确计算的首选,而且根据上一条来看,如果将浮点数转化后的整数大于18位的话,也必须用BigDecimal
时间: 2024-10-22 12:00:58

第二章 Java浮点数精确计算的相关文章

《深入理解Java虚拟机》读书笔记---第二章 Java内存区域与内存溢出异常

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来.这一章就是给大家介绍Java虚拟机内存的各个区域,讲解这些区域的作用,服务对象以及其中可能产生的问题. 1.运行时数据区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域. 1.1程序计数器 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器.在虚拟机的概念模型中里,字

Java图示(第二章 Java语言基础)

Java图示(第二章 Java语言基础) 三个基本部分:一个包声明(package语句).任意数量的引入(import语句).类和接口声明(class和interface语句) 1.包声明—package语句 1)包是类和接口的集合,即类库 2)用类库管理类,方便管理 3)Java类都在类库中 4)只有一条包声明,且为第一条 2.引入语句—import语句 1)import语句在包语句后,所有类或接口前 2)两种形式 (1)import 包名.类名: (2)import 包名*://编译器会识别

JAVA中精确计算金额BigDecimal

package com.chauvet.utils; import java.math.BigDecimal;import java.text.DecimalFormat;import java.text.NumberFormat; /*** * * 金额 * * 如果需要精确计算,必须用String来够造BigDecimal! !! * * Java里面的商业计算,不能用float和double,因为他们无法 进行精确计算. * 但是Java的设计者给编程人员提供了一个很有用的类BigDeci

BigDecimal代替浮点数精确计算用法简介

浮点数 浮点数是属于有理数中某特定子集的数的数字表示,在计算机中用以近似表示任意某个实数.具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学计数法. 浮点计算是指浮点数参与的运算,这种运算通常伴随着因为无法精确表示而进行的近似或舍入.一个浮点数a由两个数m和e来表示:a = m × b^e.在任意一个这样的系统中,我们选择一个基数b(记数系统的基)和精度p(即使用多少位来存储).m(即尾数)是形如±d.ddd...dd

第二章 Java框架整合--maven父子模块

2.1.maven父子模块 在实际开发中,我们基本都会用maven父子分模块的方式进行项目的开发. 2.2.实际操作 2.2.1.手工建立一个ssmm0的文件夹,并在该文件夹中加入一个pom.xml文件,该pom.xml文件内容如下: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmln

第二章 Java内存区域与内存溢出异常

1.程序计数器(Program Counter Register) 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 2.Java虚拟机栈(Stack)线程私有,每一个方法调用时都会创建一个栈帧(Stack Frame)用于存储局部变量表.操作数栈.动态链接.方法出口等信息.局部变量存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double).对象引用(reference类型)和returnAddress类

《深入理解Java虚拟机》笔记 第二章 Java虚拟机内存区域

? ? 这句话感觉道出了GC的本质 ? ? ? ? ? ? 1.程序计数器(Program Counter Register) ? ? 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过改为这个计数器的值来选取下一条需要执行的字节码指令,分支.循环.跳转.异常处理.线程恢复等基础功能都需要依赖这个计数器来完成. ? ? 由于Java虚拟机的多线程是通过线程轮流切换CPU时间片的方式来实现的,所以在任何一个时刻,一个处理器(对于多核处理

js浮点数精确计算(加、减、乘、除)

<SPAN style="FONT-SIZE: 18px">//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显.这个函数返回较为精确的加法结果. //调用:accAdd(arg1,arg2) //返回值:arg1加上arg2的精确结果 function accAdd(arg1,arg2){ var r1,r2,m; try{r1=arg1.toString().split(".")[1].length}catch(e){r

【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile

章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译==>Java字节码 ==通过类加载器==>JVM(jvm执行字节码)==转化为汇编指令==>CPU上执行. Java中使用的并发机制依赖于JVM的实现和CPU的指令. volatile初探 volatile是是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性.可见性