1 需求描述
- 場景1:兩個整型相除,如何保證運算結果為浮點數?如何控制運算結果的精度(小數位數)?
- 場景2:針對一個浮點數,如何控制其精度(小數位數)?
2 試驗
場景:兩整型數相除,控制運算結果、浮點數的精度
Long number1 = 110600L;
int number2 = 999;
int scale = 2;//精度n(保留n位小數)
// 【錯誤示範】
//方式1
double i = number1/number2; // 110.0 (結果錯誤,精度丟失)
//方式2
double i = (double) (number1/number2) // 110.0 (結果錯誤,精度丟失)
//方式3
BigDecimal bd1 = BigDecimal.valueOf(number1);
BigDecimal bd2 = BigDecimal.valueOf(number2);
//(可能)將報錯: java.lang.AthmeticException : Non-terminating decimal expansion; no exact representable decimal result.()
// java.lang.AthmeticException : 非終止小數展開;沒有可精確表示的十進位制結果 (意譯: 這個除法除不盡,會得到一個無限小數,導致異常。)
BigDecimal bd3 = bd1.divide(bd2).setScale( scale, RoundingMode.HALF_UP);
//【正確示範】不丟失精度
//方式4 轉換其中至少1個整型為浮點型 | 利用java運算時,運算結果型別預設等於精度最高的型別
double number3 = (double) number1/number2; //等效於 : ((double) number1)/number2 // 110.71071071071071 (結果正確,精度未丟失)
//方式5 BigDecimal + BigDecimal.divide(被除數, 精度n:=保留n位小數, 向上入|向下舍)
BigDecimal bd1 = BigDecimal.valueOf(number1);
BigDecimal bd2 = BigDecimal.valueOf(number2);
BigDecimal bd3 = bd1.divide(bd2 , scale, RoundingMode.HALF_UP);//解決方式3的異常情況
bd3.doubleValue();//輸出為浮點數 : 110.71d
bd3.toString();//輸出為字串 : "110.71"
DecimalFormat df = new DecimalFormat("#0.0000");
String dfResult = df.format(bd3);//格式化浮點數 : "110.7100"
String dfResult = df.format(110.32535235);// "110.3254" (預設會四捨五入)
3 總結
場景1:2個整數相除,計算結果預設為整型,導致精度丟失問題
方法1:至少將其中一個整型轉為浮點數,則:運算結果一定是浮點數
方法2:利用 BigDecimal + BigDecimal.divide(被除數, 精度n:=保留n位小數, 向上入|向下舍)
場景2:如何對浮點數進行四捨五入運算?
方法1: Math.round(number)
+ Math.pow(10, scale)
+ 乘法、除法運算
Math.round()
方法可以將浮點數四捨五入到最接近的整數。
如果你需要四捨五入到特定的小數位數:
- 首先,可以先將數字乘以10的n次方(n為你想要保留的小數位數)
- 然後,使用
Math.round()
進行四捨五入,最後再除以10的n次方得到結果。
double number3 = (double) number1/number2;
long x = Math.round( number3 * Math.pow(10, scale) );
double y = Math.pow(10, scale); //運算結果為浮點型
double result = x/y;//110.71
方法2: BigDecimal(number).setScale(scale, BigDecimal.ROUND_HALF_UP)
double number3 = (double) number1/number2;
BigDecimal result = new BigDecimal(number3).setScale(scale, BigDecimal.ROUND_HALF_UP);
result.doubleValue(); // 110.71
result.toString();//"110.71"
如果目標浮點數是除法運算後的結果,還可在運算過程中利用BigDecimal.divide(被除數, 精度n:=保留n位小數, 向上入|向下舍)
方法,直接在運算時控制運算結果的精度:
//核心思路: BigDecimal + BigDecimal.divide(被除數, 精度n:=保留n位小數, 向上入|向下舍)
BigDecimal bd1 = BigDecimal.valueOf(number1);
BigDecimal bd2 = BigDecimal.valueOf(number2);
BigDecimal bd3 = bd1.divide(bd2 , scale, RoundingMode.HALF_UP);
bd3.doubleValue();//輸出為浮點數 : 110.71d
bd3.toString();//輸出為字串 : "110.71"
DecimalFormat df = new DecimalFormat("#0.0000");
String dfResult = df.format(bd3);//格式化浮點數 : "110.7100"
String dfResult = df.format(110.32535235);// "110.3254" (預設會四捨五入)
方法3:String.format("%.2f")
雖然
String.format()
方法主要用於格式化字串,但它也可以用於四捨五入浮點數到指定的小數位數。
該方法不直接改變數字,而是將其格式化為包含指定小數位數的字串。
double number3 = (double) number1/number2;//110.71071071071071
String s = String.format("%.2f", number3); // "110.71"
double d = Double.parseDouble(s); // 110.71d
注意,使用
String.format()
方法時,結果是一個字串,如果你需要將其作為浮點數進行進一步操作,可以使用Double.parseDouble()將其轉換回double型別。
方法4:使用DecimalFormat
類
java.text.DecimalFormat
是NumberFormat
的一個具體子類,用於格式化十進位制數。它允許你為數字、整數和小數指定模式。
double number3 = (double) number1/number2;//110.71071071071071
DecimalFormat decimalFormat = new DecimalFormat("#.##");//保留2位小數
String s = decimalFormat.format(number3);//"110.71"
Double d = Double.parseDouble(s); // 110.71d
與
String.format()
類似,DecimalFormat的結果也是一個字串,可以透過Double.parseDouble()轉換回double型別。
X 參考文獻
- Non-terminating decimal expansion; no exact representable decimal result. - CSDN
- Java中常見的幾種四捨五入方法總結 - jb51.net