Java中的Double类型计算
⼀、问题的提出:
如果我们编译运⾏下⾯这个程序会看到什么?
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){
und(value*100)/100.0;
}
⾮常不幸,上⾯的代码并不能正常⼯作,给这个⽅法传⼊4.015它将返回4.01⽽不是4.02,如我们在上⾯看到的
4.015*100=401.49999999999994
因此如果我们要做到精确的四舍五⼊,不能利⽤简单类型做任何运算
System.out.println(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)
1package com.wetalk.wbs.bas.util;
2
bigdecimal除法保留小数3import java.io.Serializable;
4import java.math.BigDecimal;
5import java.math.RoundingMode;
6
7/**
8 * double的计算不精确,会有类似0.0000000000000002的误差,正确的⽅法是使⽤BigDecimal或者⽤整型
9 * 整型地⽅法适合于货币精度已知的情况,⽐如12.11+1.10转成1211+110计算,最后再/100即可
10 * 以下是摘抄的BigDecimal⽅法:
11*/
12public class DoubleUtil implements Serializable {
13private static final long serialVersionUID = -3345205828566485102L;
14// 默认除法运算精度
15private static final Integer DEF_DIV_SCALE = 2;
16
17/**
18 * 提供精确的加法运算。
19 *
20 * @param value1 被加数
21 * @param value2 加数
22 * @return两个参数的和
23*/
24public static Double add(Double value1, Double value2) {
25 BigDecimal b1 = new String(value1));
26 BigDecimal b2 = new String(value2));
27return b1.add(b2).doubleValue();
28 }
29
30/**
31 * 提供精确的减法运算。
32 *
33 * @param value1 被减数
34 * @param value2 减数
35 * @return两个参数的差
36*/
37public static double sub(Double value1, Double value2) {
38 BigDecimal b1 = new String(value1));
39 BigDecimal b2 = new String(value2));
40return b1.subtract(b2).doubleValue();
41 }
42
43/**
44 * 提供精确的乘法运算。
45 *
46 * @param value1 被乘数
47 * @param value2 乘数
48 * @return两个参数的积
49*/
50public static Double mul(Double value1, Double value2) {
51 BigDecimal b1 = new String(value1));
52 BigDecimal b2 = new String(value2));
53return b1.multiply(b2).doubleValue();
54 }
55
56/**
57 * 提供(相对)精确的除法运算,当发⽣除不尽的情况时,精确到⼩数点以后10位,以后的数字四舍五⼊。
58 *
59 * @param dividend 被除数
60 * @param divisor 除数
61 * @return两个参数的商
62*/
63public static Double divide(Double dividend, Double divisor) {
64return divide(dividend, divisor, DEF_DIV_SCALE);
65 }
66
67/**
68 * 提供(相对)精确的除法运算。当发⽣除不尽的情况时,由scale参数指定精度,以后的数字四舍五⼊。
69 *
70 * @param dividend 被除数
71 * @param divisor 除数
72 * @param scale 表⽰表⽰需要精确到⼩数点以后⼏位。
73 * @return两个参数的商
74*/
75public static Double divide(Double dividend, Double divisor, Integer scale) {
76if (scale < 0) {
77throw new IllegalArgumentException("The scale must be a positive integer or zero");
78 }
79 BigDecimal b1 = new String(dividend));
80 BigDecimal b2 = new String(divisor));
81return b1.divide(b2, scale,RoundingMode.HALF_UP).doubleValue();
82 }
83
84/**
85 * 提供指定数值的(精确)⼩数位四舍五⼊处理。
86 *
87 * @param value 需要四舍五⼊的数字
88 * @param scale ⼩数点后保留⼏位
89 * @return四舍五⼊后的结果
90*/
91public static double round(double value,int scale){
92if(scale<0){
93throw new IllegalArgumentException("The scale must be a positive integer or zero");
94 }
95 BigDecimal b = new String(value));
96 BigDecimal one = new BigDecimal("1");
97return b.divide(one,scale, RoundingMode.HALF_UP).doubleValue();
98 }
99 }
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论