目錄
- 數據精度問題
- BigDecimal的正確使用
- 構造陷阱
- 數值比較
- 除法
- 舍入控制 `RoundingMode`
數據精度問題
Java開發中,Double
類作為包裝類用于處理雙精度浮點數。浮點數double
無法精確表示某些十進制小數(如0.1
),導致運算結果出現誤差
double a = 0.1;
double b = 0.2;
System.out.println(a + b); // 輸出 0.30000000000000004,而非0.3
需要精確計算的場景需要使用:BigDecimal
,并通過字符串構造避免初始精度丟失。
BigDecimal d1 = new BigDecimal("0.1");
BigDecimal d2 = new BigDecimal("0.2");
System.out.println(d1.add(d2)); // 0.3
Double 比較推薦使用 Double.
compare(a, b)
double a = 0.1;
double b = 0.1;
System.out.println(a == b); // trueDouble aD = 0.1;
Double bD = 0.1;
System.out.println(aD == bD); // false
System.out.println(Double.compare(aD, bD) == 0); // true
BigDecimal的正確使用
對于需要精確計算的場景,使用BigDecimal
。
構造陷阱
避免用double
構造BigDecimal
,否則傳入的是不精確的二進制值。
double v = 0.946;
System.out.println(v); // 輸出正確,0.946
// 直接轉 BigDecimal,實際內部值是不精確的二進制值,導致截取3位小數結果錯誤
double vv = new BigDecimal(v).setScale(3, RoundingMode.DOWN).doubleValue();
System.out.println(vv); // 輸出錯誤值 0.945
正確做法:BigDecimal
(字符串構造)
double vvv = new BigDecimal(String.valueOf(v)).setScale(3, RoundingMode.DOWN).doubleValue();
System.out.println(vvv); // 輸出正確,0.946
數值比較
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("1");
System.out.println(a.compareTo(b) == 0); // true,數值相等
除法
必須指定舍入模式和精度,處理無限小數
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
// 指定保留4位小數,四舍五入
BigDecimal result = a.divide(b, 4, RoundingMode.HALF_UP); // 3.3333
不處理無限小數會拋出異常 ArithmeticException
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
a.divide(b); // 拋出 ArithmeticException
舍入控制 RoundingMode
RoundingMode.UP 遠離零方向舍入 2.5 → 3, -2.5 → -3
RoundingMode.DOWN 向零方向舍入 2.5 → 2, -2.5 → -2
RoundingMode.CEILING 向正無窮方向舍入 2.5 → 3, -2.5 → -2
RoundingMode.FLOOR 向負無窮方向舍入 2.5 → 2, -2.5 → -3
RoundingMode.HALF_UP 四舍五入(常用) 2.5 → 3, 2.4 → 2
RoundingMode.HALF_DOWN 五舍六入 2.5 → 2, 2.6 → 3
示例
// 測試數值
BigDecimal number = new BigDecimal("2.55");
String label = "正數 2.55";
int newScale = 1;System.out.println("--- 測試數值: " + label + " ---");// UP:遠離零方向舍入
System.out.println("UP : " + number.setScale(newScale, RoundingMode.UP));// DOWN:向零方向舍入
System.out.println("DOWN : " + number.setScale(newScale, RoundingMode.DOWN));// CEILING:向正無窮方向舍入
System.out.println("CEILING : " + number.setScale(newScale, RoundingMode.CEILING));// FLOOR:向負無窮方向舍入
System.out.println("FLOOR : " + number.setScale(newScale, RoundingMode.FLOOR));// HALF_UP:四舍五入
System.out.println("HALF_UP : " + number.setScale(newScale, RoundingMode.HALF_UP));// HALF_DOWN:五舍六入
System.out.println("HALF_DOWN : " + number.setScale(newScale, RoundingMode.HALF_DOWN));
結果展示,
--- 測試數值: 正數 2.55 ---
UP : 2.6
DOWN : 2.5
CEILING : 2.6
FLOOR : 2.5
HALF_UP : 2.6
HALF_DOWN : 2.5
負數注意 UP 和 CEILING,DOWN 和 FLOOR 的區別。
UP
是遠離零方向(對負數是“向下舍入”,絕對值更大)。CEILING
向正無窮方向(即對負數是“向上舍入”,更接近零),類似于數學中向上取整。
--- 測試數值: 負數 2.55 ---
UP : -2.6
DOWN : -2.5
CEILING : -2.5
FLOOR : -2.6
HALF_UP : -2.6
HALF_DOWN : -2.5