2.BigDecimal简介
BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂。因此,BigDecimal表示的数值是(unscaledValue × 10-scale)。
3.测试代码
3.1构造函数(主要测试参数类型为double和String的两个常用构造函数)
BigDecimal aDouble =new BigDecimal(1.22);
System.out.println("construct with a double value: " + aDouble);
BigDecimal aString = new BigDecimal("1.22");
System.out.println("construct with a String value: " + aString);
你认为输出结果会是什么呢?如果你没有认为第一个会输出1.22,那么恭喜你答对了,输出结果如下:
construct with a doublevalue:1.2199999999999999733546474089962430298328399658203125
construct with a String value: 1.22
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构造方法。
3、当double必须用作BigDecimal的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法,将double转换为String。要获取该结果,请使用static valueOf(double)方法。
3.2 加法操作
BigDecimal a =new BigDecimal("1.22");
System.out.println("construct with a String value: " + a);
BigDecimal b =new BigDecimal("2.22");
a.add(b);
System.out.println("aplus b is : " + a);
我们很容易会认为会输出:
construct with a Stringvalue: 1.22
a plus b is :3.44
但实际上a plus b is : 1.22
4.源码分析
4.1 valueOf(doubleval)方法
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannotfastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
returnnew BigDecimal(Double.toString(val));//见3.1关于JDK描述的第三点
}
4.2 add(BigDecimal augend)方法
public BigDecimal add(BigDecimal augend) {
long xs =this.intCompact; //整型数字表示的BigDecimal,例a的intCompact值为122
long ys = augend.intCompact;//同上
BigInteger fst = (this.intCompact !=INFLATED) ?null :this.intVal;//初始化BigInteger的值,intVal为BigDecimal的一个BigInteger类型的属性
BigInteger snd =(augend.intCompact !=INFLATED) ?null : augend.intVal;
int rscale =this.scale;//小数位数
long sdiff = (long)rscale - augend.scale;//小数位数之差
if (sdiff != 0) {//取小数位数多的为结果的小数位数
if (sdiff < 0) {
int raise =checkScale(-sdiff);
rscale =augend.scale;
if (xs ==INFLATED ||
(xs = longMultiplyPowerTen(xs,raise)) ==INFLATED)
fst =bigMultiplyPowerTen(raise);
}else {
int raise =augend.checkScale(sdiff);
if (ys ==INFLATED ||(ys =longMultiplyPowerTen(ys,raise)) ==INFLATED)
snd = augend.bigMultiplyPowerTen(raise);
}
}
if (xs !=INFLATED && ys !=INFLATED) {
long sum = xs + ys;
if ( (((sum ^ xs) &(sum ^ ys))) >= 0L)//判断有无溢出
return BigDecimal.valueOf(sum,rscale);//返回使用BigDecimal的静态工厂方法得到的BigDecimal实例
}
if (fst ==null)
fst =BigInteger.valueOf(xs);//BigInteger的静态工厂方法
if (snd ==null)
snd =BigInteger.valueOf(ys);
BigInteger sum =fst.add(snd);
return (fst.signum == snd.signum) ?new BigDecimal(sum,INFLATED, rscale, 0) :
new BigDecimal(sum,compactValFor(sum),rscale, 0);//返回通过其他构造方法得到的BigDecimal对象
}
以上只是对加法源码的分析,减乘除其实最终都返回的是一个新的BigDecimal对象,因为BigInteger与BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以a.add(b);虽然做了加法操作,但是a并没有保存加操作后的值,正确的用法应该是a=a.add(b);
5.总结
(1)商业计算使用BigDecimal。
(2)尽量使用参数类型为String的构造函数。
(3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
(4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。
JAVA中精确计算金额BigDecimal
1 package com.chauvet.utils; 2 3 import java.math.BigDecimal; 4 import java.text.DecimalFormat; 5 import java.text.NumberFormat; 6 7 /*** 8 * 9 * 金额 10 * 11 * 如果需要精确计算,必须用String来够造BigDecimal! !! 12 * 13 * Java里面的商业计算,不能用float和double,因为他们无法 进行精确计算。 14 * 但是Java的设计者给编程人员提供了一个很有用的类BigDecimal, 15 * 他可以完善float和double类无法进行精确计算的缺憾。 16 * BigDecimal类位于java.maths类包下。 17 * 它的构造函数很多,最常用的: 18 * BigDecimal(double val) 19 * BigDecimal(String str) 20 * BigDecimal(BigInteger val) 21 * BigDecimal(BigInteger unscaledVal, int scale) 22 23 * 24 * @author wxw 25 * 26 */ 27 public class AmountUtil { 28 29 /*** 30 * 保留2位小数 31 * 四舍五入 32 * @param a 33 * 34 * @return 35 * 返回一个double类型的2位小数 36 */ 37 public static Double get2Double(Double doubleVal,int scale){ 38 if(null == doubleVal){ 39 doubleVal = new Double(0); 40 } 41 return new BigDecimal(doubleVal).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 42 } 43 44 /*** 45 * 格式化Double类型并保留scale位小数 46 * 四舍五入 47 * @param doubleVal 48 * @param scale 49 * scale必须为大于0的正整数,不能等于0 50 * @return 51 */ 52 public static String formatBy2Scale(Double doubleVal,int scale){ 53 if(null == doubleVal){ 54 doubleVal = new Double(0); 55 } 56 StringBuffer sbStr = new StringBuffer("0."); 57 for (int i = 0; i < scale; i++) { 58 sbStr.append("0"); 59 } 60 DecimalFormat myformat = new DecimalFormat(sbStr.toString()); 61 return myformat.format(doubleVal); 62 } 63 64 /*** 65 * Double类型相加 + 66 67 * ROUND_HALF_UP 四舍五入 68 69 * @param val1 70 * 71 * @param val2 72 * 73 * @param scale 74 * 保留scale位小数 75 76 * @return 77 */ 78 public static Double add(Double val1,Double val2,int scale){ 79 if(null == val1){ 80 val1 = new Double(0); 81 } 82 if(null == val2){ 83 val2 = new Double(0); 84 } 85 return new BigDecimal(Double.toString(val1)).add(new BigDecimal(Double.toString(val2))).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 86 } 87 88 /*** 89 * Double类型相减 — 90 91 * ROUND_HALF_UP 四舍五入 92 93 * @param val1 94 * 95 * @param val2 96 * 97 * @param scale 98 * 保留scale位小数 99 100 * @return 101 */ 102 public static Double subtract(Double val1,Double val2,int scale){ 103 if(null == val1){ 104 val1 = new Double(0); 105 } 106 if(null == val2){ 107 val2 = new Double(0); 108 } 109 return new BigDecimal(Double.toString(val1)).subtract(new BigDecimal(Double.toString(val2))).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 110 } 111 112 /*** 113 * Double类型相乘 * 114 115 * ROUND_HALF_UP 四舍五入 116 117 * @param val1 118 * 119 * @param val2 120 * 121 * @param scale 122 * 保留scale位小数 123 124 * @return 125 */ 126 public static Double multiply(Double val1,Double val2,int scale){ 127 if(null == val1){ 128 val1 = new Double(0); 129 } 130 if(null == val2){ 131 val2 = new Double(0); 132 } 133 return new BigDecimal(Double.toString(val1)).multiply(new BigDecimal(Double.toString(val2))).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 134 } 135 136 /*** 137 * Double类型相除 / 138 139 * ROUND_HALF_UP 四舍五入 140 141 * @param val1 142 * 143 * @param val2 144 * 145 * @param scale 146 * 保留scale位小数 147 148 * @return 149 */ 150 public static Double divide(Double val1,Double val2,int scale){ 151 if(null == val1){ 152 val1 = new Double(0); 153 } 154 if(null == val2 || val2 == 0){ 155 val2 = new Double(1); 156 } 157 return new BigDecimal(Double.toString(val1)).divide(new BigDecimal(Double.toString(val2))).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue(); 158 } 159 160 161 /*** 162 * Double类型取余 % 163 164 * ROUND_HALF_UP 四舍五入 165 166 * @param val1 167 * 168 * @param val2 169 * 170 * @param scale 171 * 保留scale位小数 172 173 * @return 174 */ 175 public static int divideAndRemainder(Double val1,Double val2,int scale){ 176 if(null == val1){ 177 val1 = new Double(0); 178 } 179 if(null == val2 || val2 == 0){ 180 val2 = new Double(1); 181 } 182 return new BigDecimal(Double.toString(val1)).divideAndRemainder(new BigDecimal(Double.toString(val2)))[1].setScale(scale, BigDecimal.ROUND_HALF_UP).intValue(); 183 } 184 185 /*** 186 * 格式化Double类型数据 187 * 188 * @param val 189 * @param fmt 190 * NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用 191 * NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用 192 * @param maximumFractionDigits 193 * 如果是百分比 设置小数位数(四舍五入) 194 * @return 195 */ 196 public static String formatByNumberFormat(Double val,NumberFormat fmt,int maximumFractionDigits){ 197 if(fmt.equals(NumberFormat.getPercentInstance())){ 198 fmt.setMaximumFractionDigits(maximumFractionDigits); //百分比小数点最多3位 199 } 200 return fmt.format(val); 201 202 } 203 204 /*** 205 * 比较大小 206 * -1、0、1,即左边比右边数大,返回1,相等返回0,比右边小返回-1。 207 * @param doubleVal 208 * @return 209 */ 210 public static int compareTo(Double val1,Double val2){ 211 if(null == val1){ 212 val1 = new Double(0); 213 } 214 if(null == val2){ 215 val2 = new Double(0); 216 } 217 return new BigDecimal(val1).compareTo(new BigDecimal(val2)); 218 } 219 220 221 public static void main(String[] args) { 222 223 // System.out.println(AmountUtil.get2Double(null,3)); 224 // System.out.println(AmountUtil.add(12.2155, null,4)); 225 // System.out.println(AmountUtil.subtract(12.2155, 1D,2)); 226 // System.out.println(AmountUtil.multiply(12.2155, 2D,2)); 227 // System.out.println(AmountUtil.divide(44.13, 2D,2)); 228 // System.out.println(AmountUtil.divideAndRemainder(43D, 8D,0)); 229 // System.out.println(AmountUtil.formatByNumberFormat(0.123456, NumberFormat.getPercentInstance(),3)); 230 // System.out.println(AmountUtil.formatBy2Scale(12.23457,3)); 231 232 233 DecimalFormat df = new DecimalFormat("0.00\u2030"); //"\u2030"表示乘以1000并显示为千分数 234 System.out.println(df.format(12.1233)); //8-->1234567.89‰ 235 236 df = new DecimalFormat("0,000.0#");//在数字中添加逗号 237 System.out.println(df.format(123456789.12345)); //5-->-1,234.57 238 239 240 df = new DecimalFormat("0");//不保留小数点 四舍五入 241 System.out.println(df.format(123456789.9876)); //5-->-1,234.57 242 } 243 }
转自原文:
http://blog.csdn.net/mlin_123/article/details/51784675
http://www.cnblogs.com/chauvet/p/5832768.html
浙公网安备 33010602011771号