主要包含以下常用功能的解釋使用:
1绷旗、 BigDecimal代碼工具類(lèi)推薦
2喜鼓、 BigDecimal實(shí)際錯(cuò)誤案例分析
3、 BigDecimal計(jì)算金額正確使用方式
4衔肢、 BigDecimal使用過(guò)程中的數(shù)值格式化(例如:只包含2位小數(shù)等)
5庄岖、 BigDecima精度丟失問(wèn)題源碼及其場(chǎng)景分析
package com.example.demo.back.vo;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* @author lisanwei
*/
public class ArraysRes {
/**
* 默認(rèn)除法運(yùn)算精度
*/
private static final int DEF_DIV_SCALE = 10;
/**
* 提供精確的加法運(yùn)算
*
* @param v1 被加數(shù)
* @param v2 加數(shù)
* @return 兩個(gè)參數(shù)的和
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(String.valueOf(v1));
BigDecimal b2 = new BigDecimal(String.valueOf(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精確的加法運(yùn)算
*
* @param v1 被加數(shù)
* @param v2 加數(shù)
* @return 兩個(gè)參數(shù)的和
*/
public static BigDecimal add(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2);
}
/**
* 提供精確的加法運(yùn)算
*
* @param v1 被加數(shù)
* @param v2 加數(shù)
* @param scale 保留scale 位小數(shù)
* @return 兩個(gè)參數(shù)的和
*/
public static String add(String v1, String v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.add(b2).setScale(scale, RoundingMode.HALF_UP).toString();
}
/**
* 提供精確的減法運(yùn)算
*
* @param v1 被減數(shù)
* @param v2 減數(shù)
* @return 兩個(gè)參數(shù)的差
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精確的減法運(yùn)算。
*
* @param v1 被減數(shù)
* @param v2 減數(shù)
* @return 兩個(gè)參數(shù)的差
*/
public static BigDecimal sub(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2);
}
/**
* 提供精確的減法運(yùn)算
*
* @param v1 被減數(shù)
* @param v2 減數(shù)
* @param scale 保留scale 位小數(shù)
* @return 兩個(gè)參數(shù)的差
*/
public static String sub(String v1, String v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.subtract(b2).setScale(scale, RoundingMode.HALF_UP).toString();
}
/**
* 提供精確的乘法運(yùn)算
*
* @param v1 被乘數(shù)
* @param v2 乘數(shù)
* @return 兩個(gè)參數(shù)的積
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(String.valueOf(v1));
BigDecimal b2 = new BigDecimal(String.valueOf(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供精確的乘法運(yùn)算
*
* @param v1 被乘數(shù)
* @param v2 乘數(shù)
* @return 兩個(gè)參數(shù)的積
*/
public static BigDecimal mul(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2);
}
/**
* 提供精確的乘法運(yùn)算
*
* @param v1 被乘數(shù)
* @param v2 乘數(shù)
* @param scale 保留scale 位小數(shù)
* @return 兩個(gè)參數(shù)的積
*/
public static double mul(double v1, double v2, int scale) {
BigDecimal b1 = new BigDecimal(String.valueOf(v1));
BigDecimal b2 = new BigDecimal(String.valueOf(v2));
return round(b1.multiply(b2).doubleValue(), scale);
}
/**
* 提供精確的乘法運(yùn)算
*
* @param v1 被乘數(shù)
* @param v2 乘數(shù)
* @param scale 保留scale 位小數(shù)
* @return 兩個(gè)參數(shù)的積
*/
public static String mul(String v1, String v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.multiply(b2).setScale(scale, RoundingMode.HALF_UP).toString();
}
/**
* 提供(相對(duì))精確的除法運(yùn)算膀懈,當(dāng)發(fā)生除不盡的情況時(shí)顿锰,精確到
* 小數(shù)點(diǎn)以后10位,以后的數(shù)字四舍五入
*
* @param v1 被除數(shù)
* @param v2 除數(shù)
* @return 兩個(gè)參數(shù)的商
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
/**
* 提供(相對(duì))精確的除法運(yùn)算启搂。當(dāng)發(fā)生除不盡的情況時(shí)硼控,由scale參數(shù)指
* 定精度,以后的數(shù)字四舍五入
*
* @param v1 被除數(shù)
* @param v2 除數(shù)
* @param scale 表示表示需要精確到小數(shù)點(diǎn)以后幾位胳赌。
* @return 兩個(gè)參數(shù)的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(String.valueOf(v1));
BigDecimal b2 = new BigDecimal(String.valueOf(v2));
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* 提供(相對(duì))精確的除法運(yùn)算牢撼。當(dāng)發(fā)生除不盡的情況時(shí),由scale參數(shù)指
* 定精度疑苫,以后的數(shù)字四舍五入
*
* @param v1 被除數(shù)
* @param v2 除數(shù)
* @param scale 表示需要精確到小數(shù)點(diǎn)以后幾位
* @return 兩個(gè)參數(shù)的商
*/
public static String div(String v1, String v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v1);
return b1.divide(b2, scale, RoundingMode.HALF_UP).toString();
}
/**
* 提供精確的小數(shù)位四舍五入處理
*
* @param v 需要四舍五入的數(shù)字
* @param scale 小數(shù)點(diǎn)后保留幾位
* @return 四舍五入后的結(jié)果
*/
public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(String.valueOf(v));
return b.setScale(scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* 提供精確的小數(shù)位四舍五入處理
*
* @param v 需要四舍五入的數(shù)字
* @param scale 小數(shù)點(diǎn)后保留幾位
* @return 四舍五入后的結(jié)果
*/
public static String round(String v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(v);
return b.setScale(scale, RoundingMode.HALF_UP).toString();
}
/**
* 取余數(shù)
*
* @param v1 被除數(shù)
* @param v2 除數(shù)
* @param scale 小數(shù)點(diǎn)后保留幾位
* @return 余數(shù)
*/
public static String remainder(String v1, String v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
return b1.remainder(b2).setScale(scale, RoundingMode.HALF_UP).toString();
}
/**
* 取余數(shù) BigDecimal
*
* @param v1 被除數(shù)
* @param v2 除數(shù)
* @param scale 小數(shù)點(diǎn)后保留幾位
* @return 余數(shù)
*/
public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
return v1.remainder(v2).setScale(scale, RoundingMode.HALF_UP);
}
/**
* 比較大小
*
* @param v1 被比較數(shù)
* @param v2 比較數(shù)
* @return 如果v1 大于 v2 則 返回true 否則false
*/
public static boolean compare(String v1, String v2) {
BigDecimal b1 = new BigDecimal(v1);
BigDecimal b2 = new BigDecimal(v2);
int bj = b1.compareTo(b2);
return bj > 0;
}
}
首先請(qǐng)查看下面如圖所示執(zhí)行結(jié)果
一熏版、BigDecimal概述
Java在java.math包中提供的API類(lèi)BigDecimal,用來(lái)對(duì)超過(guò)16位有效位的數(shù)進(jìn)行精確的運(yùn)算捍掺。雙精度浮點(diǎn)型變量double可以處理16位有效數(shù)撼短,但在實(shí)際應(yīng)用中,可能需要對(duì)更大或者更小的數(shù)進(jìn)行運(yùn)算和處理
一般情況下挺勿,對(duì)于那些不需要準(zhǔn)確計(jì)算精度的數(shù)字曲横,我們可以直接使用Float和Double處理,但是和
會(huì)丟失精度不瓶。所以開(kāi)發(fā)中禾嫉,如果我們需要精確計(jì)算的結(jié)果,則必須使用BigDecimal類(lèi)來(lái)操作蚊丐。
BigDecimal所創(chuàng)建的是對(duì)象熙参,故我們不能使用傳統(tǒng)的等算術(shù)運(yùn)算符直接對(duì)其對(duì)象進(jìn)行數(shù)學(xué)運(yùn)算,而必須調(diào)用其相對(duì)應(yīng)的方法凛篙。方法中的參數(shù)也必須是BigDecimal的對(duì)象弄屡。構(gòu)造器是類(lèi)的特殊方法,專(zhuān)門(mén)用來(lái)創(chuàng)建對(duì)象鞋诗,特別是帶有參數(shù)的對(duì)象膀捷。
二、BigDecimal常用構(gòu)造函數(shù)
2.1削彬、常用構(gòu)造函數(shù)
BigDecimal(int)
創(chuàng)建一個(gè)具有參數(shù)所指定整數(shù)值的對(duì)象BigDecimal(double)
創(chuàng)建一個(gè)具有參數(shù)所指定雙精度值的對(duì)象BigDecimal(long)
創(chuàng)建一個(gè)具有參數(shù)所指定長(zhǎng)整數(shù)值的對(duì)象BigDecimal(String)
創(chuàng)建一個(gè)具有參數(shù)所指定以字符串表示的數(shù)值的對(duì)象
2.2全庸、使用問(wèn)題分析
BigDecimal a =new BigDecimal(0.1);
System.out.println("a values is:"+a);
System.out.println("=====================");
BigDecimal b =new BigDecimal("0.1");
System.out.println("b values is:"+b);
結(jié)果示例:
a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1
2.3、原因分析
1融痛、參數(shù)類(lèi)型為double的構(gòu)造方法的結(jié)果有一定的不可預(yù)知性壶笼。
有人可能認(rèn)為在Java中寫(xiě)入
所創(chuàng)建的BigDecimal正好等于,但是它實(shí)際上等于
雁刷。
這是因?yàn)?.1無(wú)法準(zhǔn)確地表示為 (或者說(shuō)對(duì)于該情況不能表示為任何有限長(zhǎng)度的二進(jìn)制小數(shù))覆劈。
這樣傳入到構(gòu)造方法的值不會(huì)正好等于 0.1(雖然表面上等于該值)
2、String 構(gòu)造方法是完全可預(yù)知的:寫(xiě)入將創(chuàng)建一個(gè)
它正好等于預(yù)期的 0.1。因此比較而言 通常建議優(yōu)先使用String構(gòu)造方法责语。
3炮障、當(dāng)double必須用作BigDecimal的源時(shí),請(qǐng)注意此構(gòu)造方法提供了一個(gè)準(zhǔn)確轉(zhuǎn)換坤候;它不提供與以下操作相同的結(jié)果:先使用方法胁赢,然后使用BigDecimal(String)構(gòu)造方法,將double轉(zhuǎn)換為String白筹。
三智末、BigDecimal常用方法詳解
3.1、常用方法
「add(BigDecimal)」
BigDecimal對(duì)象中的值相加徒河,返回BigDecimal對(duì)象「subtract(BigDecimal)」
BigDecimal對(duì)象中的值相減系馆,返回BigDecimal對(duì)象「multiply(BigDecimal)」
BigDecimal對(duì)象中的值相乘,返回BigDecimal對(duì)象「divide(BigDecimal)」
BigDecimal對(duì)象中的值相除顽照,返回BigDecimal對(duì)象「toString()」
將BigDecimal對(duì)象中的值轉(zhuǎn)換成字符串「doubleValue()」
將BigDecimal對(duì)象中的值轉(zhuǎn)換成雙精度數(shù)「floatValue()」
將BigDecimal對(duì)象中的值轉(zhuǎn)換成單精度數(shù)「longValue()」
將BigDecimal對(duì)象中的值轉(zhuǎn)換成長(zhǎng)整數(shù)「intValue()」
將BigDecimal對(duì)象中的值轉(zhuǎn)換成整數(shù)
3.2由蘑、BigDecimal大小比較
** java中對(duì)BigDecimal比較大小一般用的是bigdemical的compareTo方法**
int a = bigdemical.compareTo(bigdemical2)
返回結(jié)果分析
a = -1,表示bigdemical小于bigdemical2;
a = 0,表示bigdemical等于bigdemical2棒厘;
a = 1,表示bigdemical大于bigdemical2纵穿;
舉例:a大于等于b。
new bigdemica(a).compareTo(new bigdemical(b)) >= 0
四奢人、BigDecimal格式化
由于NumberFormat類(lèi)的format()方法可以使用BigDecimal對(duì)象作為其參數(shù)
可以利用BigDecimal對(duì)超出16位有效數(shù)字的【貨幣值谓媒、百分值、以及一般數(shù)值】進(jìn)行格式化控制何乎。
以利用BigDecimal對(duì)貨幣和百分比格式化為例句惯,代碼示例如下:
①首先創(chuàng)建BigDecimal對(duì)象,進(jìn)行BigDecimal的算術(shù)運(yùn)算后支救,分別建立對(duì)貨幣和百分比格式化的引用
②最后利用BigDecimal對(duì)象作為format()方法的參數(shù)抢野,輸出其格式化的貨幣值和百分比
// 建立貨幣格式化引用
NumberFormat currency = NumberFormat.getCurrencyInstance();
// 建立百分比格式化引用
NumberFormat percent = NumberFormat.getPercentInstance();
// 百分比小數(shù)點(diǎn)最多3位
percent.setMaximumFractionDigits(3);
// 數(shù)字格式化
NumberFormat numberInstance = NumberFormat.getNumberInstance();
// 百分比小數(shù)點(diǎn)最多2位
numberInstance.setMaximumFractionDigits(2);
//貸款金額
BigDecimal loanAmount = new BigDecimal("15000.48");
//利率
BigDecimal interestRate = new BigDecimal("0.008");
//相乘
BigDecimal interest = loanAmount.multiply(interestRate);
System.out.println("貸款金額:" + currency.format(loanAmount));
System.out.println("利率:" + percent.format(interestRate));
System.out.println("利息:" + currency.format(interest));
String format = numberInstance.format(0.56988);
System.out.println("format = " + format);
執(zhí)行結(jié)果
BigDecimal格式化保留2為小數(shù),不足則補(bǔ)0:
package com.example.demo.back.vo;
import java.math.BigDecimal;
import java.text.DecimalFormat;
/**
* @author lisanwei
*/
public class ArraysRes {
public static void main(String[] args) {
System.out.println(formatToNumber(new BigDecimal("3.435")));
System.out.println(formatToNumber(new BigDecimal(0)));
System.out.println(formatToNumber(new BigDecimal("0.00")));
System.out.println(formatToNumber(new BigDecimal("0.001")));
System.out.println(formatToNumber(new BigDecimal("0.006")));
System.out.println(formatToNumber(new BigDecimal("0.206")));
}
/**
* @desc 1.0~1之間的BigDecimal小數(shù)各墨,格式化后失去前面的0,則前面直接加上0指孤。
* 2.傳入的參數(shù)等于0,則直接返回字符串"0.00"
* 3.大于1的小數(shù)贬堵,直接格式化返回字符串
* @param obj 傳入的小數(shù)
* @return
*/
public static String formatToNumber(BigDecimal obj) {
DecimalFormat df = new DecimalFormat("#.00");
if(obj.compareTo(BigDecimal.ZERO)==0) {
return "0.00";
}else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
return "0"+df.format(obj).toString();
}else {
return df.format(obj).toString();
}
}
}
五恃轩、BigDecimal常見(jiàn)異常
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
六、BigDecimal總結(jié)
6.1黎做、總結(jié)
在需要精確的小數(shù)計(jì)算時(shí)再使用BigDecimal叉跛,BigDecimal的性能比double和float差,在處理龐大蒸殿,復(fù)雜的運(yùn)算時(shí)尤為明顯筷厘。故一般精度的計(jì)算沒(méi)必要使用BigDecimal鸣峭。盡量使用參數(shù)類(lèi)型為String的構(gòu)造函數(shù)。BigDecimal都是不可變的(immutable)的酥艳, 在進(jìn)行每一次四則運(yùn)算時(shí)摊溶,都會(huì)產(chǎn)生一個(gè)新的對(duì)象 ,所以在做加減乘除運(yùn)算時(shí)要記得要保存操作后的值玖雁。
BigDecimal 在金額計(jì)算中會(huì)丟失精度更扁,示例如下:
通過(guò)測(cè)試發(fā)現(xiàn)盖腕,當(dāng)使用 double 或者 float 這些浮點(diǎn)數(shù)據(jù)類(lèi)型時(shí)赫冬,會(huì)丟失精度,String溃列、int 則不會(huì)劲厌,這是為什么呢?
查看源碼會(huì)看見(jiàn)long valBits = Double.doubleToLongBits(val)這個(gè)方法