来考虑这样一种情况,先来看代码:

public class Test {
public static void main(String[] args) {
System.out.println(0.4 + 0.8); // = 1.2 ?
System.out.println(2 - 1.1); // = 0.9 ?
System.out.println(0.2 * 3); // = 0.6 ?
System.out.println(1.2 / 3); // = 0.4 ?
}
}

也许你会天真的认为,第一行打印 1.2,第二行打印 0.9,第三行打印 0.6,第四行打印 0.4,因为依据多年的数学惯性逻辑输出结果与预期的一致是很理所当然的事情嘛!
但是当程序跑完之后,输出的结果与预期的大有出入,来看下后台打印的结果:

1.2000000000000002
0.8999999999999999
0.6000000000000001
0.39999999999999997

结果看到这样的结果,是不是很让人郁闷呢?当然了,这些数据是我故意挑的,并不是所有涉及浮点数的运算操作都会算出这样预期之外的结果,但是一件很明了的事情就是,
当操作涉及浮点数运算的时候,我们一定要谨防这样的事情发生。
上面代码中,加也好,减也好,乘也好,除也好,它们都是属于 double 级别的运算,那为什么会打印输出这样的结果呢?原因是,并不是所有的浮点数都能够被精确的表示成一个
double 类型值,有些浮点数值不能够被精确的表示成 double 类型值,因此它会被表示成与它最接近的 double 类型的值。所以很遗憾,0.4 + 0.8 ≠ 1.2 。……
怎么来解决这个问题呢?在 java 中提供了一个 BigDecimal 类来执行精确小数的计算,BigDecimal 类提供了以下操作:算术、标度操作、舍入、比较、哈希算法和格式转换。
BigDecimal 类提供的方法:
加法:add
减法:subtract
乘法:multiply
除法:divide

下面用 BigDecimal 类改写上面的代码实现:

public class Test {
public static void main(String[] args) {
System.out.println(new BigDecimal(0.4).add(new BigDecimal(0.8)));// = 1.2 ?
System.out.println(new BigDecimal(2).subtract(new BigDecimal(1.1)));// = 0.9 ?
System.out.println(new BigDecimal(0.2).multiply(new BigDecimal(3)));// = 0.6 ?
System.out.println(new BigDecimal(1.2).divide(new BigDecimal(3)));// = 0.4 ?
}
}

也许你正在查类 BigDecimal 的 API,API 上对类 BigDecimal 有一大串的文字说明,也许你还没来得及看完,但能够确定的是,类 BigDecimal 确实能够准确保证精确小数的执行,
那我上面代码的注释是不是忘记去掉了啊?不是。要是真这么干,那就大祸了。先来看一下后台的打印输出结果:

1.20000000000000006661338147750939242541790008544921875
0.899999999999999911182158029987476766109466552734375
0.600000000000000033306690738754696212708950042724609375
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1603)
at example.BigDecimalApp.main(BigDecimalApp.java:20)

如果真这么玩了,你会看到结果更恶心了,这还不算,而且还抛了异常,这是为什么呢?别急,来看一下 API 上是怎么说的:

public BigDecimal(double val)
    将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。 
注:

1. 此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double (或者说对于该情况,不能表示为任何有限长度的二进制小数),这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

2. 另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法
3. 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,
然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。

以上文字摘自 API,API 上解释的很清楚了,这里就不多说了,API 建议优先使用 String 构造方法,那我们就来试一下呗:

public class Test {
public static void main(String[] args) { System.out.println(new BigDecimal("0.4").add(new BigDecimal("0.8"))); // = 1.2 √
System.out.println(new BigDecimal("2").subtract(new BigDecimal("1.1"))); // = 0.9 √
System.out.println(new BigDecimal("0.2").multiply(new BigDecimal("3")));// = 0.6 √
System.out.println(new BigDecimal("1.2").divide(new BigDecimal("3"))); // = 0.4 √ }
}

后台打印输出结果:

1.2
0.9
0.6
0.4

OK,这下子终于不出篓子了,所以千万不能随随便便使用 BigDecimal(double) 构造器来创建 BigDecimal 对象,因为该构造器是根据它的参数的精确值来创建实例对象的,
该构造方法的结果还是有一定的不可预知性,用 BigDecimal(String) 此构造器来创建 BigDecimal 实例那就不会有问题了。
以上提到了类 BigDecimal 中的 add 、subtract 、multiply 、divide 方法,在 API 中,你可以看到,这几个方法都各自有自己的一个重载方法,如:
add (BigDecimal augend, MathContext mc)  ……
第二个参数 mc 是什么意思呢?先来看一段代码:

public class Test {
public static void main(String[] args) {
//计算结果保留三有效数字
System.out.println(new BigDecimal(Math.PI + "").add(new BigDecimal("0.89842"), new MathContext(3))); //输出 4.04
}
}

第二个参数 mc 是用来保留计算结果的有效位数的,其他三个方法的重载用法是一样的,这里就不一 一列出来了。

Refer:

http://www.blogjava.net/fancydeepin/archive/2012/08/29/java_bigdecimal.html

BigDecimal 执行精确小数计算的更多相关文章

  1. 【Java】使用BigDecimal类进行精确小数计算

    在商业计算中(尤其是计算价格)需要使用BigDecimal类来进行精确小数计算,因为用其他类型计算(如double)得到的结果不是精确的! 写个测试类. import org.junit.Test; ...

  2. Python:执行精确的浮点数运算

    需要对浮点数执行精确的计算操作,并且不希望有任何小误差的出现. 浮点数的一个普遍问题是它们并不能精确的表示十进制数.并且,即使是最简单的数学运算也会产生小的误差,比如: >>> a ...

  3. Python之执行精确的浮点数运算

    有时候:代码上数字计算可能会有如同下面的误差 原因: 这些错误是由底层CPU和IEEE 754标准通过自己的浮点单位去执行算术时的特征. 由于Python的浮点数据类型使用底层表示存储数据,因此你没办 ...

  4. 使用BigDecimal进行精确计算工具类

    package com.develop.util; import java.math.BigDecimal; import java.math.RoundingMode; public class M ...

  5. 电商网站中价格的精确计算(使用BigDecimal进行精确运算(实现加减乘除运算))

    使用BigDecimal的String的构造器.商业计算中,使用bigdecimal的String构造器,一定要用. 重要的事情说三遍: 商业计算中,使用bigdecimal的String构造器! 商 ...

  6. (转)使用BigDecimal进行精确运算

    场景:在进行支付业务的金额计算时,通常采用BigDecimal类型的数据,并没有看到常见的int double类型,所以有必要好好学习下BigDecimal的常用用法. 1 误区 首先我们先来看如下代 ...

  7. Java编程的逻辑 (5) - 小数计算为什么会出错?

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  8. java用double和float进行小数计算精度不准确

    java用double和float进行小数计算精度不准确 大多数情况下,使用double和float计算的结果是准确的,但是在一些精度要求很高的系统中或者已知的小数计算得到的结果会不准确,这种问题是非 ...

  9. BigDecimal进行精确运算demo工具类

    package com.js.ai.modules.pointwall.interfac; import java.math.BigDecimal; public class TestDigDecim ...

随机推荐

  1. Graves of the Internet - 互联网坟墓

    Graves of the Internet - 互联网坟墓 互联网公司逝去产品列表 以此祭奠那些夕阳下的奔跑,祭奠那些逝去的青春 演示地址 点击 这里 https://myvin.github.io ...

  2. 解决IE的背景颜色透明子元素不透明问题

    假设背景为黑色,70%半透明,对需要半透明的层用如下CSS background-color: #000000; /* background color for IE */ filter: alpha ...

  3. Geoffrey Hinton获得IEEE的麦克斯韦奖的颁奖辞

    2016年6月IEEE的麦克斯韦奖颁发给了机器学习的领军人物Geoffrey Hinton.颁奖辞十分优雅,同时简洁.凝练地解释了机器学习的最新进展以及神经网络的崛起.我忍不住翻译了一下. 颁奖辞 G ...

  4. ASP.NET WebAPI 06 HttpMessageHandler管道

    HttpMessageHandler管道 在Web API的中,微软为了更好的进行架构扩展,采用的了一套管道设计----HttpMessageHander(其实WCF也有类似架构). 在整个管道中的头 ...

  5. 【LOJ】#2544. 「JXOI2018」游戏

    题解 九条可怜还有那么善良的一面??? 显然有些数在这个区间里没有数是它的约数,它们其中的最后一个取的一定就是\(t(p)\)的值 这样我们只需要枚举\(t(p)\)的值,这个值就是"没有任 ...

  6. Oracle中生成uuid的方法

    Oracle中生成uuid的方法 下载LOFTER客户端 在Oracle SQL 提供了一个生成uuid的函数sys_guid: http://download.oracle.com/docs/cd/ ...

  7. jquery放大镜非常漂亮噢

    这个放大镜的代码挺简单滴效果也不错. <script> //QQ:496928838 微凉 $(function(){ $("#demo").enlarge( { // ...

  8. 【转】eval()函数用法

    eval 功能:将字符串str当成有效的表达式来求值并返回计算结果. 语法: eval(source[, globals[, locals]]) -> value 参数: source:一个Py ...

  9. CF815C Karen and Supermarket [树形DP]

    题目传送门 Karen and Supermarket On the way home, Karen decided to stop by the supermarket to buy some gr ...

  10. Mutex 的正确打开方式

    在使用 Mutex 在给线程/进程间加锁时,需要注意的问题. 1 AbandonedMutexException 在使用 mutex.WaitOne 时,可能抛出异常 AbandonedMutexEx ...