以前,只是聽說過,Java裏面有些個類型,在計算的時候,是不能想當然的,因爲,計算機的實現法和你自己的想法是不一樣的。當時,沒怎麼在意,後來,還真聽一個哥們說,他把錢給算錯了。既然遇到了,我就稍微做個筆記,以後遇到類似問題,自己也好有個印象,
先看奇葩現象。
private static void testDouble() {
Double d = 0.81d;
System.out.println(d);
PrintUtil.divideLine();
System.out.println("0.05 + 0.01 = " + (0.05 + 0.01));//0.060000000000000005
System.out.println("1.0 - 0.42 = " + (1.0 - 0.42));//0.5800000000000001
System.out.println("4.015 * 100 = " + (4.015 * 100));//401.49999999999994
System.out.println("123.3 / 100 = " + (123.3 / 100));//1.2329999999999999
System.out.println(new DecimalFormat("0.00").format(4.025d));//4.03 四捨五入
}
就簡單在意下,上面的幾個double類型的數據的計算結果就好啦。其他都是寫打印個分界符啥的代碼。我弄個工具類,使用方便些。
可以看到,這些 double的計算,果然跟想象的有點不一樣。
就像,他們說的4.999999999999999在計算機看來,可買不到,標價爲5。0000塊錢的東西。那就出問題啦。
這個時候,就要用到一個叫 BigDecimal 的類啦。
下面看一個工具類的代碼
package com.lxk.util;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 精確計算 double and float
* Created by lxk on 2017/9/27
*/
public class DoubleUtil implements Serializable {
private static final long serialVersionUID = -3345205828566485102L;
// 默認除法運算精度
private static final Integer DEF_DIV_SCALE = 2;
/**
* 提供精確的加法運算。
*
* @param value1 被加數
* @param value2 加數
* @return 兩個參數的和
*/
public static Double add(Double value1, Double value2) {
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.add(b2).doubleValue();
}
/**
* 提供精確的減法運算。
*
* @param value1 被減數
* @param value2 減數
* @return 兩個參數的差
*/
public static double sub(Double value1, Double value2) {
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精確的乘法運算。
*
* @param value1 被乘數
* @param value2 乘數
* @return 兩個參數的積
*/
public static Double mul(Double value1, Double value2) {
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相對)精確的除法運算,當發生除不盡的情況時, 精確到小數點以後10位,以後的數字四捨五入。
*
* @param dividend 被除數
* @param divisor 除數
* @return 兩個參數的商
*/
public static Double divide(Double dividend, Double divisor) {
return divide(dividend, divisor, DEF_DIV_SCALE);
}
/**
* 提供(相對)精確的除法運算。 當發生除不盡的情況時,由scale參數指定精度,以後的數字四捨五入。
*
* @param dividend 被除數
* @param divisor 除數
* @param scale 表示表示需要精確到小數點以後幾位。
* @return 兩個參數的商
*/
public static Double divide(Double dividend, Double divisor, Integer scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(dividend));
BigDecimal b2 = new BigDecimal(Double.toString(divisor));
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* 提供指定數值的(精確)小數位四捨五入處理。
*
* @param value 需要四捨五入的數字
* @param scale 小數點後保留幾位
* @return 四捨五入後的結果
*/
public static double round(double value, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(value));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
}
}
下面是,這個工具類的實際使用代碼,爲了操作對比方便,我就不嫌囉嗦的把代碼再複製一遍吧
/**
* double的一些計算奇葩現象,試驗一把,就印象深刻啦。
*/
private static void testDouble() {
Double d = 0.81d;
System.out.println(d);
PrintUtil.divideLine();
System.out.println("0.05 + 0.01 = " + (0.05 + 0.01));//0.060000000000000005
System.out.println("1.0 - 0.42 = " + (1.0 - 0.42));//0.5800000000000001
System.out.println("4.015 * 100 = " + (4.015 * 100));//401.49999999999994
System.out.println("123.3 / 100 = " + (123.3 / 100));//1.2329999999999999
System.out.println(new DecimalFormat("0.00").format(4.025d));//4.03 四捨五入
}
/**
* 精確計算
*/
private static void testDoubleExact() {
System.out.println("0.05 + 0.01 = " + DoubleUtil.add(0.05, 0.01));
System.out.println("1.0 - 0.42 = " + DoubleUtil.sub(1.0, 0.42));
System.out.println("4.015 * 100 = " + DoubleUtil.mul(4.015, 100d));
System.out.println("123.3 / 100 = " + DoubleUtil.divide(123.3, 100d));//保留兩位
System.out.println("123.3 / 100 = " + DoubleUtil.divide(123.3, 100d, 3));//保留三位
System.out.println(DoubleUtil.round(4.025d, 2));
}
這就省略了main方法啦。
下面看上述代碼的運行結果。
這個不是什麼高科技,但是得有個印象,知道有這麼個問題存在,不然,那就真得跟我那哥們一樣,等到真正出問題啦,才知道,錢這麼算是有大問題的。
上面的事Java代碼裏面的,
下面看JavaScript裏面的。
這個世界上沒有知識是學不會的,不是嗎?如果一開始學不會,就可以把問題細化分解,然後學習更基本的知識。最後,所有問題都能變得和1+1=2一樣簡單,我們需要的只是時間。好了,最後給大家推薦一個學習Java的好網站JAVA自學網站–how2j.cn