1.引言
float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦。
先看下面代码
public static void main(String[] args) { System.out.println(0.2 + 0.1); System.out.println(0.3 - 0.1); System.out.println(0.2 * 0.1); System.out.println(0.3 / 0.1); }
运行结果如下
你认为你看错了,但结果却是是这样的。问题在哪里呢?原因在于我们的计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。浮点数的值实际上是由一个特定的数学公式计算得到的。
其实java的float只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算。
2.BigDecimal构造方法
1.public BigDecimal(double val) 将double表示形式转换为BigDecimal *不建议使用
2.public BigDecimal(int val) 将int表示形式转换成BigDecimal
3.public BigDecimal(String val) 将String表示形式转换成BigDecimal
为什么不建议采用第一种构造方法呢?来看例子
public static void main(String[] args) { BigDecimal bigDecimal = new BigDecimal(2); BigDecimal bDouble = new BigDecimal(2.3); BigDecimal bString = new BigDecimal("2.3"); System.out.println("bigDecimal=" + bigDecimal); System.out.println("bDouble=" + bDouble); System.out.println("bString=" + bString); }
运行结果如下
为什么会出现这种情况呢?
JDK的描述:1、参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
2、另一方面,String 构造方法是完全可预知的:写入 newBigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法。
当double必须用作BigDecimal的源时,请使用Double.toString(double)
转成String,然后
使用String构造方法,或使用BigDecimal的静态方法valueOf,如下
public static void main(String[] args) { BigDecimal bDouble1 = BigDecimal.valueOf(2.3); BigDecimal bDouble2 = new BigDecimal(Double.toString(2.3)); System.out.println("bDouble1=" + bDouble1); System.out.println("bDouble2=" + bDouble2); }
结果如下
3.BigDecimal加减乘除运算
对于常用的加,减,乘,除,BigDecimal类提供了相应的成员方法。
public BigDecimal add(BigDecimal value); //加法 public BigDecimal subtract(BigDecimal value); //减法 public BigDecimal multiply(BigDecimal value); //乘法 public BigDecimal divide(BigDecimal value); //除法
大概的用法如下
public static void main(String[] args) { BigDecimal a = new BigDecimal("4.5"); BigDecimal b = new BigDecimal("1.5"); System.out.println("a + b =" + a.add(b)); System.out.println("a - b =" + a.subtract(b)); System.out.println("a * b =" + a.multiply(b)); System.out.println("a / b =" + a.divide(b)); }
运行结果
这里有一点需要注意的是除法运算divide.
BigDecimal除法可能出现不能整除的情况,比如 4.5/1.3,这时会报错java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
其实divide方法有可以传三个参数
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
第一参数表示除数, 第二个参数表示小数点后保留位数,
第三个参数表示舍入模式,只有在作除法运算或四舍五入时才用到舍入模式,有下面这几种
ROUND_CEILING //向正无穷方向舍入 ROUND_DOWN //向零方向舍入 ROUND_FLOOR //向负无穷方向舍入 ROUND_HALF_DOWN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5 ROUND_HALF_EVEN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN ROUND_HALF_UP //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6 ROUND_UNNECESSARY //计算结果是精确的,不需要舍入模式 ROUND_UP //向远离0的方向舍入
按照各自的需要,可传入合适的第三个参数。四舍五入采用 ROUND_HALF_UP
需要对BigDecimal进行截断和四舍五入可用setScale方法,例:
public static void main(String[] args) { BigDecimal a = new BigDecimal("4.5635"); a = a.setScale(3, RoundingMode.HALF_UP); //保留3位小数,且四舍五入 System.out.println(a); }
*减乘除其实最终都返回的是一个新的BigDecimal对象,因为BigInteger与BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象
public static void main(String[] args) { BigDecimal a = new BigDecimal("4.5"); BigDecimal b = new BigDecimal("1.5"); a.add(b); System.out.println(a); //输出4.5. 加减乘除方法会返回一个新的BigDecimal对象,原来的a不变 }
4.总结
(1)商业计算使用BigDecimal。
(2)尽量使用参数类型为String的构造函数。
(3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
(4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。
附DoubleUtils类
package com.cocoadd.diamond.utils; import java.math.BigDecimal; public class DoubleUtils { /** * 对double数据进行取精度. * * @param value * @return 精度计算后的数据. BigDecimal.setScale()方法用于格式化小数点 * setScale(1)表示保留一位小数,默认用四舍五入方式 * setScale(1,BigDecimal.ROUND_DOWN)直接删除多余的小数位,如2.35会变成2.3 * setScale(1,BigDecimal.ROUND_UP)进位处理,2.35变成2.4 * setScale(1,BigDecimal.ROUND_HALF_UP)四舍五入,2.35变成2.4 * setScaler(1,BigDecimal.ROUND_HALF_DOWN)四舍五入,2.35变成2.3,如果是5则向下舍 */ public static double round(double value) { BigDecimal bd = new BigDecimal(value); bd = bd.setScale(2, BigDecimal.ROUND_DOWN); double d = bd.doubleValue(); bd = null; return d; } /** * double 相加 * * @param d1 * @param d2 * @return */ public static double sum(double d1, double d2) { BigDecimal bd1 = new BigDecimal(Double.toString(d1)); BigDecimal bd2 = new BigDecimal(Double.toString(d2)); return bd1.add(bd2).setScale(2, BigDecimal.ROUND_DOWN).doubleValue(); } /** * double 相减 * * @param d1 * @param d2 * @return */ public static double sub(double d1, double d2) { BigDecimal bd1 = new BigDecimal(Double.toString(d1)); BigDecimal bd2 = new BigDecimal(Double.toString(d2)); return bd1.subtract(bd2).setScale(2, BigDecimal.ROUND_DOWN) .doubleValue(); } /** * double 乘法 * * @param d1 * @param d2 * @return */ public static double mul(double d1, double d2) { BigDecimal bd1 = new BigDecimal(Double.toString(d1)); BigDecimal bd2 = new BigDecimal(Double.toString(d2)); return bd1.multiply(bd2).setScale(2, BigDecimal.ROUND_DOWN) .doubleValue(); } /** * double 除法 * * @param d1 * @param d2 * @param scale * 四舍五入 小数点位数 * @return */ public static double div(double d1, double d2) { // 当然在此之前,你要判断分母是否为0, // 为0你可以根据实际需求做相应的处理 BigDecimal bd1 = new BigDecimal(Double.toString(d1)); BigDecimal bd2 = new BigDecimal(Double.toString(d2)); return bd1.divide(bd2, 2, BigDecimal.ROUND_DOWN).doubleValue(); } }
相关推荐
Java中BigDecimal的基本运算详解 Java中的BigDecimal是一种高精度的数据类型,它可以用来表示非常大的整数和小数,提供了丰富的数学运算功能。下面我们将对Java中BigDecimal的基本运算进行详细的介绍。 构造方法 ...
Java BigDecimal详解 BigDecimal 是 Java 中的一个类,它由任意精度的整数非标度值和 32 位的整数标度 (scale) 组成。它主要用于商业计算、银行存款数额等场合,需要提供完全精确的结果。 知识点 1:BigDecimal 的...
Java中BigDecimal类的使用详解 Java中BigDecimal类是Java.math包中提供的一个API类,用于对超过16位有效位的数进行精确的运算。由于浮点数的精度问题,Java中浮点数的计算会失去一定的精确度。因此,使用BigDecimal...
Java中的`BigDecimal`类是用来表示任意精度的十进制数,尤其适合于需要精确计算的商业和财务场景。它的核心概念包括非标度值(unscaled value)和标度(scale),非标度值是一个任意精度的整数,标度则是小数点后的...
### BigDecimal详解、代码示例和经常遇到的坑 #### 一、BigDecimal概述 `BigDecimal`是Java编程语言中`java.math`包内提供的一种高级数据类型,主要用于处理高精度的十进制数值。与基本数据类型`float`或`double`...
1. **使用`BigDecimal`**:对于需要高精度计算的场景,推荐使用`BigDecimal`来代替`double`或`float`。`BigDecimal`提供了丰富的构造函数和方法来支持高精度计算。 2. **避免直接比较浮点数**:不要直接比较两个...
在Java编程语言中,当我们处理涉及货币、财务或者任何需要高精度计算的场景时,`BigDecimal` 类是非常重要的工具之一。本文将深入探讨 `BigDecimal` 类的基本概念、特点以及如何使用它来进行精确的算术运算。 #### ...
为了解决这种问题,可以使用`BigDecimal`来进行高精度计算: ```java BigDecimal x = new BigDecimal("23.5455"); BigDecimal result = x.setScale(2, RoundingMode.HALF_UP); // 输出结果为:"23.55" System.out....
对于涉及到大数的计算问题,Java提供了`BigInteger`和`BigDecimal`类来处理高精度计算。 - `BigInteger`用于整数的高精度计算。 - `BigDecimal`用于小数的高精度计算。 常用的方法有:`add()`, `subtract()`, `...
### JAVA解惑知识点详解 #### 一、表达式谜题:奇数判断 **知识点概述:** 本节探讨了一个常见的编程问题:如何准确地判断一个整数是否为奇数。通过对一个具体的Java代码示例进行分析,揭示了取余运算在处理负数时...
Java 四舍五入方法详解 Java 四舍五入方法是指在 Java 语言中对数字进行四舍五入操作的方法。四舍五入是一种常用的数字截断方法,经常用于金融、科学计算和其他领域。 在 Java 语言中,四舍五入方法可以通过多种...
### Java解惑知识点详解 #### 一、表达式谜题概览 本书《Java解惑》专注于探索Java编程语言及核心类库中的复杂细节。...- 对于需要高精度计算的情况,考虑使用 `BigDecimal` 类,它可以提供任意精度的计算。
### Java 学习笔记知识点详解 #### 一、在命令行下执行 Java 1. **编译 Java 文件**: - 使用 `javac` 命令编译 `.java` 文件,生成 `.class` 文件(字节码文件)。 - 示例命令:`javac 文件名.java` - 注意...
### Java考试复习资料详解 #### 一、架构模式:B/S与C/S - **B/S架构**(Browser/Server,浏览器/服务器)是一种通过Web浏览器进行访问的应用系统架构。客户端只需要一个Web浏览器即可,无需安装任何软件。所有的...
- **解决方法**: 使用 `BigDecimal` 类来进行高精度计算。 #### 20. 浮点数的严格模式 - **严格浮点数 (`strictfp`)**: 该关键字可以用来确保浮点数运算的一致性和可预测性。 #### 21. 对象比较 - **对象引用比较*...
### Java解惑知识点详解 #### 一、表达式谜题:判断奇数的正确方法 在探讨本书提及的第一个“表达式谜题”时,我们首先遇到了一个用来判断一个整数是否为奇数的方法: ```java public static boolean isOdd(int i...
- 对于需要高精度计算的场景,如金融计算,可以使用`BigInteger`和`BigDecimal`。 #### 十一、初始化 Java为所有成员变量提供了默认初始化,但需要注意的是,局部变量不会得到这样的处理,必须手动初始化。 #### ...
5. **大数值对象**:虽然不是基本类型,但Java提供了`BigInteger`和`BigDecimal`类用于处理超出基本类型范围的大整数和高精度浮点数。 #### 二、变量和常量 1. **变量**: - 变量是存储数据的一种方式。在Java中...