Java Double型別精確運算解決
阿新 • • 發佈:2018-12-19
Java中的簡單浮點數型別float和double不能夠進行運算。
首先我們想到的是四捨五入,Math類中的round方法不能設定保留幾位小數,我們只能像這樣(例如保留兩位)
Math.round(value * 100) / 100.0;
非常不幸,上面的程式碼不能解決包含小數位精度問題,類似400.0000000009,或者整數差0.00000000001的問題。
其次我們會使用BigDecimal,使用他的4個構造方法即加、減、乘、除。
當然想到這裡,也差不多能解決上面的問題了
解決方案:
import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.regex.Pattern; public final strictfp class MathUtil { private MathUtil() { } // 預設運算精度 private static int DEF_SCALE = 10; /** * 提供資料型別轉換為BigDecimal * * @param object 原始資料 * @return BigDecimal */ public static final BigDecimal bigDecimal(Object object) { if (object == null) { throw new NullPointerException(); } BigDecimal result; try { result = new BigDecimal(String.valueOf(object).replaceAll(",", "")); } catch (NumberFormatException e) { throw new NumberFormatException("Please give me a numeral.Not " + object); } return result; } /** * 提供(相對)精確的加法運算。 * * @param num1 被加數 * @param num2 加數 * @return 兩個引數的和 */ public static final Double add(Object num1, Object num2) { BigDecimal result = bigDecimal(num1).add(bigDecimal(num2)); return result.setScale(DEF_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供(相對)精確的減法運算。 * * @param num1 被減數 * @param num2 減數 * @return 兩個引數的差 */ public static final Double subtract(Object num1, Object num2) { BigDecimal result = bigDecimal(num1).subtract(bigDecimal(num2)); return result.setScale(DEF_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供(相對)精確的乘法運算。 * * @param num1 被乘數 * @param num2 乘數 * @return 兩個引數的積 */ public static final Double multiply(Object num1, Object num2) { BigDecimal result = bigDecimal(num1).multiply(bigDecimal(num2)); return result.setScale(DEF_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供(相對)精確的除法運算,當發生除不盡的情況時,精度為10位,以後的數字四捨五入。 * * @param num1 被除數 * @param num2 除數 * @return 兩個引數的商 */ public static final Double divide(Object num1, Object num2) { return divide(num1, num2, DEF_SCALE); } /** * 提供(相對)精確的除法運算。 當發生除不盡的情況時,由scale引數指定精度,以後的數字四捨五入。 * * @param num1 被除數 * @param num2 除數 * @param scale 表示表示需要精確到小數點以後幾位。 * @return 兩個引數的商 */ public static final Double divide(Object num1, Object num2, Integer scale) { if (scale == null) { scale = DEF_SCALE; } num2 = num2 == null || Math.abs(new Double(num2.toString())) == 0 ? 1 : num2; if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal result = bigDecimal(num1).divide(bigDecimal(num2), scale, BigDecimal.ROUND_HALF_UP); return result.doubleValue(); } /** * 提供精確的小數位四捨五入處理。 * * @param num 需要四捨五入的數字 * @param scale 小數點後保留幾位 * @return 四捨五入後的結果 */ public static final Double round(Object num, int scale) { if (scale < 0) { throw new IllegalArgumentException("The scale must be a positive integer or zero"); } BigDecimal result = bigDecimal(num).divide(bigDecimal("1"), scale, BigDecimal.ROUND_HALF_UP); return result.doubleValue(); } /** * 獲取start到end區間的隨機數,不包含start+end * * @param start * @param end * @return */ public static final BigDecimal getRandom(int start, int end) { return new BigDecimal(start + Math.random() * end); } /** * 格式化 * * @param obj * @param pattern * @return */ public static final String format(Object obj, String pattern) { if (obj == null) { return null; } if (pattern == null || "".equals(pattern)) { pattern = "#"; } DecimalFormat format = new DecimalFormat(pattern); return format.format(bigDecimal(obj)); } /** 是否數字 */ public static final boolean isNumber(Object object) { Pattern pattern = Pattern.compile("\\d+(.\\d+)?$"); return pattern.matcher(object.toString()).matches(); }
這裡需要將結果round再次保留2位小數
MathUtil.round(MathUtil.add("100.03", "232.134"), 2);
以上解決Java Double精確運算的問題。