Java使用BigDecimal進行高精度計算的示例代碼
首先看如下代碼示例:
System.out.println(0.05 + 0.01); System.out.println(0.05 - 0.03); System.out.println(1.025 * 100); System.out.println(305.1 / 1000);
輸出結(jié)果為:
0.060000000000000005
0.020000000000000004
102.49999999999999
0.30510000000000004
Java語言支持兩種基本的浮點類型:float和double,以及與它們對應(yīng)的包裝類Float和Double。它們都依據(jù)IEEE 754 標(biāo)準(zhǔn),該標(biāo)準(zhǔn)為 32 位浮點和 64 位雙精度浮點二進制小數(shù)定義了二進制標(biāo)準(zhǔn)。
IEEE 754 用科學(xué)記數(shù)法以底數(shù)為 2 的小數(shù)來表示浮點數(shù)。IEEE 浮點數(shù)用 1 位表示數(shù)字的符號,用 8 位來表示指數(shù),用 23 位來表示尾數(shù),即小數(shù)部分,作為有符號整數(shù)的指數(shù)可以有正負(fù)之分,小數(shù)部分用二進制(底數(shù) 2)小數(shù)來表示
不要用浮點值表示精確值
一些非整數(shù)值(如幾美元和幾美分這樣的小數(shù))需要很精確。浮點數(shù)不是精確值,所以使用它們會導(dǎo)致舍入誤差。因此,使用浮點數(shù)來試圖表示象貨幣量這樣的精確數(shù)量不是一個好的想法。使用浮點數(shù)來進行美元和美分計算會得到災(zāi)難性的后果。浮點數(shù)最好用來表示象測量值這類數(shù)值,這類值從一開始就不怎么精確。
使用BigDecimal
從 JDK 1.3 起,Java 開發(fā)人員就有了另一種數(shù)值表示法來表示非整數(shù): BigDecimal 。 BigDecimal 是標(biāo)準(zhǔn)的類,在編譯器中不需要特殊支持,它可以表示任意精度的小數(shù),并對它們進行計算。
用于加、減、乘和除的方法給 BigDecimal 值提供了算術(shù)運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產(chǎn)生新的 BigDecimal 對象。因此,因為創(chuàng)建對象的開銷, BigDecimal 不適合于大量的數(shù)學(xué)計算,但設(shè)計它的目的是用來精確地表示小數(shù)。如果您正在尋找一種能精確表示如貨幣量這樣的數(shù)值,則 BigDecimal 可以很好地勝任該任務(wù)。
構(gòu)造 BigDecimal 數(shù)
對于 BigDecimal ,有幾個可用的構(gòu)造函數(shù)。其中一個構(gòu)造函數(shù)以雙精度浮點數(shù)作為輸入,另一個以整數(shù)和換算因子作為輸入,還有一個以小數(shù)的 String 表示作為輸入。要小心使用 BigDecimal(double) 構(gòu)造函數(shù),因為如果不了解它,會在計算過程中產(chǎn)生舍入誤差。請使用基于整數(shù)或 String 的構(gòu)造函數(shù)。
public class Test { public static void main(String[] args) { // 以雙精度浮點數(shù)進行構(gòu)造 BigDecimal bd1 = new BigDecimal(0.5); BigDecimal bd2 = new BigDecimal(0.1); System.out.println(bd1.add(bd2)); // 以String類型進行構(gòu)造 BigDecimal bd3 = new BigDecimal("0.5"); BigDecimal bd4 = new BigDecimal("0.1"); System.out.println(bd3.add(bd4)); } }
輸出結(jié)果為:
0.6000000000000000055511151231257827021181583404541015625
0.6
上面代碼分別以
BigDecimal(double val) BigDecimal(String val)
不同的方式進行構(gòu)造 BigDecimal 數(shù),輸出的結(jié)果是不一樣的。
回到最開始的示例,提供工具類進行精確的浮點數(shù)運算,包括加減乘除和四舍五入。
import java.math.BigDecimal; public class ArithUtil { private static final int DEF_DIV_SCALE = 6; // 默認(rèn)除法運算精度 /** * 提供精確的加法運算。 * * @param v1 被加數(shù) * @param v2 加數(shù) * @return 兩個參數(shù)的和 */ public static double add(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); } /** * 提供精確的減法運算。 * * @param v1 被減數(shù) * @param v2 減數(shù) * @return 兩個參數(shù)的差 */ public static double sub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); } /** * 提供精確的乘法運算。 * * @param v1 被乘數(shù) * @param v2 乘數(shù) * @return 兩個參數(shù)的積 */ public static double mul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); } /** * 提供(相對)精確的除法運算,當(dāng)發(fā)生除不盡的情況時,精確到 小數(shù)點以后10位,以后的數(shù)字四舍五入。 * * @param v1 被除數(shù) * @param v2 除數(shù) * @return 兩個參數(shù)的商 */ public static double div(double v1, double v2) { return div(v1, v2, DEF_DIV_SCALE); } /** * 提供(相對)精確的除法運算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指 定精度,以后的數(shù)字四舍五入。 * * @param v1 被除數(shù) * @param v2 除數(shù) * @param scale 表示表示需要精確到小數(shù)點以后幾位。 * @return 兩個參數(shù)的商 */ public static double div(double v1, double v2, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供精確的小數(shù)位四舍五入處理。 * * @param v 需要四舍五入的數(shù)字 * @param scale 小數(shù)點后保留幾位 * @return 四舍五入后的結(jié)果 */ public static double round(double v, int scale) { if (scale < 0) { throw new IllegalArgumentException( "The scale must be a positive integer or zero"); } BigDecimal b = new BigDecimal(Double.toString(v)); BigDecimal one = new BigDecimal("1"); return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue(); } }
結(jié)束語:
在 Java 程序中使用浮點數(shù)和小數(shù)充滿著陷阱。浮點數(shù)和小數(shù)不象整數(shù)一樣“循規(guī)蹈矩”,不能假定浮點計算一定產(chǎn)生整型或精確的結(jié)果,雖然它們的確“應(yīng)該”那樣做。最好將浮點運算保留用作計算本來就不精確的數(shù)值,譬如測量。如果需要表示定點數(shù)(譬如,幾美元和幾美分),則使用 BigDecimal 。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
java中的equals()和toString()方法實例詳解
這篇文章主要介紹了java中的equals()和toString()方法實例詳解的相關(guān)資料,這里舉例說明,并附實例代碼,和實現(xiàn)效果圖,需要的朋友可以參考下2016-11-11TraceIdPatternLogbackLayout日志攔截源碼解析
這篇文章主要為大家介紹了TraceIdPatternLogbackLayout日志攔截源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11Java流程控制之循環(huán)結(jié)構(gòu)for,增強for循環(huán)
這篇文章主要介紹了Java流程控制之循環(huán)結(jié)構(gòu)for,增強for循環(huán),for循環(huán)是編程語言中一種循環(huán)語句,而循環(huán)語句由循環(huán)體及循環(huán)的判定條件兩部分組成,其表達式為:for(單次表達式;條件表達式;末尾循環(huán)體){中間循環(huán)體;},下面我們倆看看文章內(nèi)容的詳細(xì)介紹2021-12-12淺談java.util.concurrent包中的線程池和消息隊列
這篇文章主要介紹了淺談java.util.concurrent包中的線程池和消息隊列,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Kotlin-Coroutines中的async與await異步協(xié)程管理
這篇文章主要為大家介紹了Kotlin-Coroutines中的async與await異步協(xié)程管理,提升程序性能解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10