JAVA的double型數據以及float類型的數據均不能進行精確計算,許多編程語言也是同樣,這與計算機的底層原理有關。java
所以計算得出的結果每每超出預期。編程
尤爲是在金融行業,計算價格或者銀行業務的錢的計算。精確計算變得尤其重要。編程語言
雖然咱們能夠經過四捨五入的方式來處理結果,可是這樣作就意味着存在着偏差。ide
好比說下面這些計算,我知道結果應該是精確的數字,計算機並無計算出咱們想要的結果。工具
/** * @author wzm * @version 1.0.0 * @date 2020/1/25 14:24 **/ public class MathTest { public static void main(String[] args) { System.out.println(0.05 + 0.01); System.out.println(1.0 - 0.43); System.out.println(2.03 * 10); System.out.println(3.3 / 10); } }
計算結果:url
0.060000000000000005 0.5700000000000001 20.299999999999997 0.32999999999999996
Java中提供精確計算的類。java.math.BigDecimalspa
import java.math.BigDecimal; /** * @author wzm * @version 1.0.0 * @date 2020/1/25 14:24 **/ public class BigDecTest { public static void main(String[] args) { BigDecimal a = new BigDecimal("10"); BigDecimal b = new BigDecimal("5"); BigDecimal c; //加法 c = a.add(b); System.out.println("加法運算:" + c); //減法 c = a.subtract(b); System.out.println("加法運算:" + c); //除法 c = a.multiply(b); System.out.println("除法運算:" + c); //乘法 c = a.divide(b, BigDecimal.ROUND_CEILING); System.out.println("乘法運算:" + c); } }
BigDecimal v1 = new BigDecimal("-1"); BigDecimal v2 = new BigDecimal("3"); int r = v1.compareTo(v2); System.out.println(r); * if r==0 --> v1等於v2 * if r==1 --> v1大於v2 * if r==-1 --> v1小於v2
BigDecimal定義瞭如下舍入模式,只有在作除法運算或四捨五入時纔會用到舍入模式。code
ROUND_CEILINGblog
ROUND_CEILING模式是向正無窮大方向舍入。結果往較大的數值靠。ip
import java.math.BigDecimal; /** * @author wzm * @version 1.0.0 * @date 2020/1/25 14:24 **/ public class BigDecTest { public static void main(String[] args) { int a = 2; int b = BigDecimal.ROUND_CEILING; System.out.println(new BigDecimal("1.01").setScale(a, b)); System.out.println(new BigDecimal("1.0100").setScale(a, b)); System.out.println(new BigDecimal("1.011").setScale(a, b)); System.out.println(new BigDecimal("1.01001").setScale(a, b)); System.out.println(new BigDecimal("1.014").setScale(a, b)); System.out.println(new BigDecimal("-1.01").setScale(a, b)); System.out.println(new BigDecimal("-1.0100").setScale(a, b)); System.out.println(new BigDecimal("-1.011").setScale(a, b)); System.out.println(new BigDecimal("-1.01001").setScale(a, b)); System.out.println(new BigDecimal("-1.014").setScale(a, b)); } }
結果:
1.01 1.01 1.02 1.02 1.02 -1.01 -1.01 -1.01 -1.01 -1.01
ROUND_FLOOR
ROUND_FLOOR模式是向負無窮大方向舍入。結果往較小的數值靠。
import java.math.BigDecimal; /** * @author wzm * @version 1.0.0 * @date 2020/1/25 14:24 **/ public class BigDecTest { public static void main(String[] args) { int a = 2; int b = BigDecimal.ROUND_FLOOR; System.out.println(new BigDecimal("1.01").setScale(a, b)); System.out.println(new BigDecimal("1.0100").setScale(a, b)); System.out.println(new BigDecimal("1.011").setScale(a, b)); System.out.println(new BigDecimal("1.01001").setScale(a, b)); System.out.println(new BigDecimal("1.014").setScale(a, b)); System.out.println(new BigDecimal("-1.01").setScale(a, b)); System.out.println(new BigDecimal("-1.0100").setScale(a, b)); System.out.println(new BigDecimal("-1.011").setScale(a, b)); System.out.println(new BigDecimal("-1.01001").setScale(a, b)); System.out.println(new BigDecimal("-1.014").setScale(a, b)); } }
結果:
1.01 1.01 1.01 1.01 1.01 -1.01 -1.01 -1.02 -1.02 -1.02
ROUND_DOWN
ROUND_DOWN模式是向靠近零的方向舍入。
ROUND_UP
ROUND_UP模式是向遠離零的方向舍入。
ROUND_ UNNECESSARY
ROUND_ UNNECESSARY模式是不使用舍入模式。若是能夠確保計算結果是精確的,則能夠指定此模式,不然若是指定了使用此模式卻遇到了不精確的計算結果,則拋出ArithmeticException。
ROUND_HALF_DOWN
ROUND_HALF_DOWN模式是向距離最近的一邊舍入,若是兩邊距離相等,就向靠近零的方向舍入。
ROUND_HALF_UP
ROUND_HALF_UP模式是向距離最近的一邊舍入,若是兩邊距離相等,就向遠離零的方向舍入。這個模式在實際的場景中比較經常使用。
ROUND_HALF_EVEN
ROUND_HALF_UP模式是向距離最近的一邊舍入,若是兩邊距離相等且小數點後保留的位數是奇數,就使用ROUND_HALF_UP模式;若是兩邊距離相等且小數點後保留的位數是偶數,就使用ROUND_HALF_DOWN模式。
import java.math.BigDecimal; import java.math.RoundingMode; /** * java精確計算工具 * * @author wzm * @version 1.0.0 * @date 2020/1/25 14:15 **/ public class BigDecUtils { /** * 提供精確加法計算的add方法(整數運算) */ public static String add(String val1, String val2) { return add(val1, val2, 0, 0); } /** * 提供精確加法計算的add方法(默認四捨五入) * * @param val1 被加數 * @param val2 加數 * @param scale 精確範圍(小數點後幾位) */ public static String add(String val1, String val2, int scale) { return add(val1, val2, scale, BigDecimal.ROUND_HALF_UP); } /** * 提供精確加法計算的add方法 * * @param val1 被加數 * @param val2 加數 * @param scale 精確範圍(小數點後幾位) * @param roundMode 精確模式 */ public static String add(String val1, String val2, int scale, int roundMode) { BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); BigDecimal result = b1.add(b2); // mode爲0,則不須要精確 if (roundMode != 0) { result = result.setScale(scale, roundMode); } return result.toString(); } /** * 提供精確減法運算的subtract方法 * * @param val1 被減數 * @param val2 減數 * @return 兩個參數的差 */ public static String sub(String val1, String val2) { return sub(val1, val2, 0, 0); } /** * 提供精確減法運算的subtract方法(默認四捨五入) * * @param val1 被減數 * @param val2 減數 * @param scale 精確範圍(小數點後幾位) */ public static String sub(String val1, String val2, int scale) { return sub(val1, val2, scale, BigDecimal.ROUND_HALF_UP); } /** * 提供精確減法運算的subtract方法 * * @param val1 被減數 * @param val2 減數 * @param scale 精確範圍(小數點後幾位) * @param roundMode 精確模式 */ public static String sub(String val1, String val2, int scale, int roundMode) { BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); BigDecimal result = b1.subtract(b2); // mode爲0,則不須要精確 if (roundMode != 0) { result = result.setScale(scale, roundMode); } return result.toString(); } /** * 提供精確的除法運算方法divide * * @param val1 被除數 * @param val2 除數 */ public static String div(String val1, String val2) throws IllegalAccessException { return div(val1, val2, 0, null); } /** * 提供精確的除法運算方法divide(默認四捨五入) * * @param val1 被除數 * @param val2 除數 * @param scale 精確範圍(小數點後幾位) */ public static String div(String val1, String val2, int scale) throws IllegalAccessException { return div(val1, val2, scale, RoundingMode.HALF_UP); } /** * 提供精確的除法運算方法divide * * @param val1 被除數 * @param val2 除數 * @param scale 精確範圍(小數點後幾位) * @param roundingMode 精確模式 */ public static String div(String val1, String val2, int scale, RoundingMode roundingMode) throws IllegalAccessException { // 若是精確範圍小於0,拋出異常信息 if (scale < 0) { throw new IllegalAccessException("精確度不能小於0"); } BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); // roundingMode爲null,則不須要精確 if (roundingMode != null) { return Double.toString(b1.divide(b2, scale, roundingMode).doubleValue()); } else { return Double.toString(b1.divide(b2, 0).doubleValue()); } } /** * 提供精確乘法運算的multiply方法 * * @param val1 被乘數 * @param val2 乘數 * @return 兩個參數的積 */ public static String mul(String val1, String val2) { return mul(val1, val2, 0, 0); } /** * 提供精確乘法運算的multiply方法(默認四捨五入) * * @param val1 被乘數 * @param val2 乘數 * @param scale 精確範圍(小數點後幾位) */ public static String mul(String val1, String val2, int scale) { return mul(val1, val2, scale, BigDecimal.ROUND_HALF_UP); } /** * 提供精確乘法運算的multiply方法 * * @param val1 被乘數 * @param val2 乘數 * @param scale 精確範圍(小數點後幾位) * @param roundMode 舍入模式 */ public static String mul(String val1, String val2, int scale, int roundMode) { BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); BigDecimal result = b1.multiply(b2); // mode爲0,則不須要精確 if (roundMode != 0) { result = result.setScale(scale, roundMode); } return result.toString(); } /** * 比較大小 :返回較大的那個 * * @param val1 v1 * @param val2 v2 */ public static String max(String val1, String val2) { BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); return Double.toString(b1.max(b2).doubleValue()); } /** * 比較大小 :返回較小的那個 * * @param val1 v1 * @param val2 v2 */ public static String min(String val1, String val2) { BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); return Double.toString(b1.min(b2).doubleValue()); } /** * 比較大小 * if(r==0) v1等於v2 * if(r==1) v1大於v2 * if(r==-1) v1小於v2 * * @param val1 v1 * @param val2 v2 * @param scale 精確範圍 * @param roundMode 舍入模式 */ public static int compare(String val1, String val2, int scale, int roundMode) { BigDecimal b1 = new BigDecimal(val1); BigDecimal b2 = new BigDecimal(val2); BigDecimal result = b1.subtract(b2); // mode爲0,則不須要精確 if (roundMode != 0) { result = result.setScale(scale, roundMode); } return result.compareTo(BigDecimal.ZERO); } public static void main(String[] args) throws IllegalAccessException { System.out.println(add("10", "5")); System.out.println(sub("10", "5")); System.out.println(mul("10", "5")); System.out.println(div("10", "5")); System.out.println(compare("-10", "5", 2, BigDecimal.ROUND_HALF_UP)); System.out.println(max("10", "5")); System.out.println(min("10", "5")); BigDecimal v1 = new BigDecimal("-1"); BigDecimal v2 = new BigDecimal("3"); int r = v1.compareTo(v2); System.out.println(r); } }