大家好,欢迎来到IT知识分享网。
一、问题的提出:
如果我们编译运行下面这个程序会看到什么?
public class Test{
public static void main(String args[]){
System.out.println(0.05+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(123.3/100);
}
};
你没有看错!结果确实是
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。
这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。
在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。
四舍五入
我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位):
public double round(double value){
return Math.round(value*100)/100.0;
}
非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的
4.015*100=401.49999999999994
因此如果我们要做到精确的四舍五入,不能利用简单类型做任何运算
java.text.DecimalFormat也不能解决这个问题:
System.out.println(new java.text.DecimalFormat(“0.00”).format(4.025));
输出是4.02
二、精确计算
在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用 java.math.BigDecimal
我们如果需要精确计算,非要用String来够造BigDecimal不可!在《Effective Java》一书中的例子是用String来够造BigDecimal的
下面提供计算的代码:
(注意:divide方法中推荐使用枚举RoundingMode.HALF_UP)
1 packagecom.wetalk.wbs.bas.util;2
3 importjava.io.Serializable;4 importjava.math.BigDecimal;5 importjava.math.RoundingMode;6
7 /**
8 * double的计算不精确,会有类似0.0000000000000002的误差,正确的方法是使用BigDecimal或者用整型9 * 整型地方法适合于货币精度已知的情况,比如12.11+1.10转成1211+110计算,最后再/100即可10 * 以下是摘抄的BigDecimal方法:11 */
12 public class DoubleUtil implementsSerializable {13 private static final long serialVersionUID = -3345205828566485102L;14 //默认除法运算精度
15 private static final Integer DEF_DIV_SCALE = 2;16
17 /**
18 * 提供精确的加法运算。19 *20 *@paramvalue1 被加数21 *@paramvalue2 加数22 *@return两个参数的和23 */
24 public staticDouble add(Double value1, Double value2) {25 BigDecimal b1 = newBigDecimal(Double.toString(value1));26 BigDecimal b2 = newBigDecimal(Double.toString(value2));27 returnb1.add(b2).doubleValue();28 }29
30 /**
31 * 提供精确的减法运算。32 *33 *@paramvalue1 被减数34 *@paramvalue2 减数35 *@return两个参数的差36 */
37 public static doublesub(Double value1, Double value2) {38 BigDecimal b1 = newBigDecimal(Double.toString(value1));39 BigDecimal b2 = newBigDecimal(Double.toString(value2));40 returnb1.subtract(b2).doubleValue();41 }42
43 /**
44 * 提供精确的乘法运算。45 *46 *@paramvalue1 被乘数47 *@paramvalue2 乘数48 *@return两个参数的积49 */
50 public staticDouble mul(Double value1, Double value2) {51 BigDecimal b1 = newBigDecimal(Double.toString(value1));52 BigDecimal b2 = newBigDecimal(Double.toString(value2));53 returnb1.multiply(b2).doubleValue();54 }55
56 /**
57 * 提供(相对)精确的除法运算,当发生除不尽的情况时, 精确到小数点以后10位,以后的数字四舍五入。58 *59 *@paramdividend 被除数60 *@paramdivisor 除数61 *@return两个参数的商62 */
63 public staticDouble divide(Double dividend, Double divisor) {64 returndivide(dividend, divisor, DEF_DIV_SCALE);65 }66
67 /**
68 * 提供(相对)精确的除法运算。 当发生除不尽的情况时,由scale参数指定精度,以后的数字四舍五入。69 *70 *@paramdividend 被除数71 *@paramdivisor 除数72 *@paramscale 表示表示需要精确到小数点以后几位。73 *@return两个参数的商74 */
75 public staticDouble divide(Double dividend, Double divisor, Integer scale) {76 if (scale < 0) {77 throw new IllegalArgumentException(“The scale must be a positive integer or zero”);78 }79 BigDecimal b1 = newBigDecimal(Double.toString(dividend));80 BigDecimal b2 = newBigDecimal(Double.toString(divisor));81 returnb1.divide(b2, scale,RoundingMode.HALF_UP).doubleValue();82 }83
84 /**
85 * 提供指定数值的(精确)小数位四舍五入处理。86 *87 *@paramvalue 需要四舍五入的数字88 *@paramscale 小数点后保留几位89 *@return四舍五入后的结果90 */
91 public static double round(double value,intscale){92 if(scale<0){93 throw new IllegalArgumentException(“The scale must be a positive integer or zero”);94 }95 BigDecimal b = newBigDecimal(Double.toString(value));96 BigDecimal one = new BigDecimal(“1”);97 returnb.divide(one,scale, RoundingMode.HALF_UP).doubleValue();98 }99 }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/21536.html