JAVA BigDecimal 類使用總計及閉坑指南

概念

BigDecimal是一個不可變的,任意精度的有符號十進制數(shù)

BigDecimal由任意精度的整數(shù)非標度值和32位的整數(shù)標度(scale)組成。如果為零或整數(shù)樟澜,則標度是小數(shù)點后的位數(shù)误窖。如果為負數(shù),則將該數(shù)的非標度值乘以10的負scale次冪秩贰。因此霹俺,BigDecimal表示的數(shù)值是(unscaledValueX10 ̄scale)。

BigDecimal對象內通過BigInteger IntVal存儲傳遞對象數(shù)字部分毒费,通過int scale記錄小數(shù)點位數(shù)丙唧,通過int precision記錄有效位數(shù)(默認為0)。

BigDecimal的加減乘除就成了BigInteger與BigInteger之間的加減乘除觅玻,浮點數(shù)的計算也轉化為整形的計算想际,可以大大提供性能,并且通過BigInteger可以保存大數(shù)字溪厘,從而實現(xiàn)真正十大進制的計算胡本,在整個計算過程中,還涉及scale的判斷和precision判斷從而確定最終輸出結果畸悬。

????????????在Java中侧甫,由CPU原生提供的整型最大范圍是64位long型整數(shù)。使用long型整數(shù)可以通過CPU指令進行計算蹋宦,速度會非撑冢快。果使用的整數(shù)范圍超過了long型冷冗,BigInteger就是用來表示任意大小的整數(shù)守屉,BigInteger內部用一個int[]數(shù)組來模擬一個非常大的數(shù)。

類中屬性

scale:有多少位小數(shù)(即小數(shù)點后有多少位)

precision:一共有多少位數(shù)字蒿辙,在確定了precision后就會要求結合Rounding Mode做一些舍入方面的操作

intVal和scale胸梆,分別表示BigDecimal的無標度值和標度敦捧,BigDecimal可以表示為一個任意精度的無標度值和一個32位整型的標度

intCompact:字符串去掉小數(shù)點后,轉為long的值碰镜,如果intVal在compact的過程中超過了Long.MAX_VAULE則將intCompact記為Long.MIN_VALUE

intVal:當傳的字符串長度大于等于18時才使用BigInteger表示數(shù)字

stringCache:在toString方法的時候用到

BigDecimal 繼承了Number并實現(xiàn)了Comparable接口兢卵。

繼承了Number,將會提供將表示的數(shù)值轉換為byteValue()绪颖、shortValue()秽荤、intValue()、longValue()柠横、floatValue()窃款、doubleValue()的方法

實現(xiàn)了Comparable接口,可以使用compareTo()方法來進行比較

問題:

使用newBigDecimal(double)的方式計算精確數(shù)值牍氛,會有精度缺失的問題晨继,使用BigDecimal.valueOf(double)的方式就不會存在此問題。

具體原因:

float與double類型主要是為了科學計算和工程計算而設計搬俊。為了在廣泛的數(shù)值范圍上提供較為精確的快速近和計算而精心設計的紊扬,并沒有完全精確的結果,所以不應該被用于精確結果的場合唉擂。

????????new BigDecimal()源碼


BigDecimal.valueOf()源碼


不同的地方在于valueOf()方法在對double類型轉換的時候餐屎,做了一次轉換為字符串的操作,從而避免了不準確的問題玩祟;但是如果是float類型腹缩,那么此方法又會出現(xiàn)不準確的問題。

參數(shù)類型為int空扎,long藏鹊,字符串時,這兩種方式?jīng)]有任何區(qū)別转锈。

正確的做法是自己構造BigDecimal對象時伙判,使用String.valueOf()將參數(shù)顯示轉換為字符串。官方注釋也有說明此問題黑忱,使用new BigDecimal(double val)方法得到的結果是不可預知的宴抚,推薦使用入?yún)㈩愋蜑镾tring的構造函數(shù)來進行浮點數(shù)的精確計算。

當入?yún)㈩愋蜑閒loat時甫煞,使用valueOf()方法以及new BigDecimal()構造方法會出現(xiàn)浮點運算精度不一致的問題菇曲。使用String.valueOf(String val)方法,運算結果正確抚吠。

valueOf()方法對參數(shù)進行一次double的轉換常潮,9.9f會被強制轉換為double類型,所以會出現(xiàn)精度問題楷力。

BigDecimal比較問題(equals與compareTo)

問題:當入?yún)閟tring類型時喊式,會出現(xiàn)此問題孵户。如果入?yún)㈩愋筒粸閟tring類型,并不會出現(xiàn)此問題岔留,但是會有精度不準確的影響夏哭。

使用BigDecimal(“0.00”).equals(BigDecimal.ZERO)與BigDecimal(0.00).equals(BigDecimal.ZERO)區(qū)別

解決方案:可以使用compareTo(BigDecimal.ZERO)==0,來判斷是否等于0

結論:對于BigDecimal的大小比較献联,使用equals方法的話不僅會比較值的大小竖配,還會比較兩個對象的精確度,而compareTo方法數(shù)值相同精度不同也會被視認為相等里逆。

compareTo()方法中的注釋有所體現(xiàn)进胯。


使用方式案例

常用構造函數(shù)

BigDecimal(int):創(chuàng)建一個具有參數(shù)所指定整數(shù)值的對象

BigDecimal(double):創(chuàng)建一個具有參數(shù)所指定雙精度值的對象

BigDecimal(long):創(chuàng)建一個具有參數(shù)所指定長整數(shù)值的對象

Bigdecimal(String):創(chuàng)建一個具有參數(shù)所指定以字符串表示的數(shù)值的對象

常用方法

add(BigDecimal):BigDecimal對象中的值相加,返回BigDecimal對象

substract(BigDecimal):BigDecimal對象中的值相減原押,返回BigDecimal對象

multiply(BigDecimal):BigDecimal對象中的值相乘胁镐,返回BigDecimal對象

divide(BigDecimal):相除

doubleValue():轉雙精度

longValue():轉長整型

intValue():轉整型

floatValue():轉單精度

toString():將BigDecimal對象中的值轉換成字符串

BigDecimal大小比較

BigDecimal比較大小一般用的是compareTo方法

BigDecimal big=new BigDecimal(5.2);

int i = big.compareTo(new BigDecimal(2.2));

System.out.println(i);


BigDecimal.ROUND_DOWN

ROUND_DOWN:直接省略掉指定位數(shù)后的內容

BigDecimal b=new? ? BigDecimal("2.2345").setScale(2,Bigdecimal.ROUND_DOWN); // 2.23


BigDecimal.ROUND_UP

// 直接對指定位數(shù)后的內容做進一位處理

BigDecimal b=new BigDecimal("2.2355").setScale(2,Bigdecimal.ROUND_DOWN);// 2.24


BigDecimal.ROUND_CEILING

// 正數(shù)使用ROUND_UP規(guī)則,負數(shù)使用ROUND_DOWN規(guī)則

BigDecimal b=new BigDecimal("2,125446").setScale(2,BigDecimal.ROUND_CEILING);// 2.13

BigDecimal b=new BigDecimal("-2.1253456").setScale(2,BigDecimal.ROUND_CEILING);// -2.12


BigDecimal.ROUND_FLOOR

// 正數(shù)省略內容诸衔,負數(shù)向下進一位

BigDecimal b=new BigDecimal("2.125456").setScale(2,BigDecimal.ROUND_FLOOR);// 2.12

BigDecimal b=new BigDecimal("-2.125456").setScale(2,BigDecimal.ROUND_FLOOR);// -2.13


BigDecimal.ROUND_HALF_UP BigDecimal.ROUND_HALF_DOWN 四舍五入

BigDecimal b=new BigDecimal("2.125456").setScale(2,BigDecimal.ROUND_HALF_UP);// 2.13

BigDecimal b=new BigDecimal("-2.125456").setScale(2,BigDecimal.ROUND_HALF_DOWN);// -2.13


BigDecimal.ROUND_HALF_EVEN

指定小數(shù)位的前一位盯漂,如果是奇數(shù)則四舍五入后進位,如果是偶數(shù)則舍棄指定小數(shù)位后面內容

BigDecimal bigDecimalA = new BigDecimal("2.113").setScale(2, BigDecimal.ROUND_HALF_EVEN);

// 2.11? 雖然是奇數(shù)署隘,但是3<5宠能,不會進位

BigDecimal bigDecimalB = new BigDecimal("2.115").setScale(2, BigDecimal.ROUND_HALF_EVEN);

// 2.12? 因為是奇數(shù)且符合"五入"亚隙,則進位

此舍入模式也稱為“銀行家舍入法”磁餐,主要在美國使用。四舍六入阿弃,五分兩種情況诊霹。

如果前一位為奇數(shù),則入位渣淳,否則舍去脾还。


ROUND_UNNECESSARY

斷言請求的操作具有精確的結果,因此不需要舍入

如果對獲得精確結果的操作指定此舍入模式入愧,則拋出ArithmeticException


其他注意問題

在使用divide方法進行除法時當不整除鄙漏,出現(xiàn)無限循環(huán)小數(shù)時,就會拋異常棺蛛。

解決方法:divide方法設置精確的小數(shù)點怔蚌,divide(xxxxx,2)

小總結

不要隨便使用BigDecimal,一般精度的計算沒必要使用BigDecimal。因為BigDecimal的精度比double和float性能差旁赊。在處理龐大復雜的計算尤為明顯桦踊。

盡量使用參數(shù)類型為String的構造函數(shù)

BigDecimal都是不可變的(immutable)的,在進行每一次四則運算時终畅,都會產(chǎn)生一個新的對象籍胯,所以在做加減乘除運算時要記得保存操作后的值

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末竟闪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杖狼,更是在濱河造成了極大的恐慌炼蛤,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件本刽,死亡現(xiàn)場離奇詭異鲸湃,居然都是意外死亡,警方通過查閱死者的電腦和手機子寓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門暗挑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人斜友,你說我怎么就攤上這事炸裆。” “怎么了鲜屏?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵烹看,是天一觀的道長。 經(jīng)常有香客問我洛史,道長惯殊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任也殖,我火速辦了婚禮土思,結果婚禮上,老公的妹妹穿的比我還像新娘忆嗜。我一直安慰自己己儒,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布捆毫。 她就那樣靜靜地躺著闪湾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绩卤。 梳的紋絲不亂的頭發(fā)上途样,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音濒憋,去河邊找鬼何暇。 笑死,一個胖子當著我的面吹牛跋炕,可吹牛的內容都是我干的赖晶。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼遏插!你這毒婦竟也來了捂贿?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤胳嘲,失蹤者是張志新(化名)和其女友劉穎厂僧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體了牛,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡颜屠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鹰祸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甫窟。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蛙婴,靈堂內的尸體忽然破棺而出粗井,到底是詐尸還是另有隱情,我是刑警寧澤街图,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布浇衬,位于F島的核電站,受9級特大地震影響餐济,放射性物質發(fā)生泄漏耘擂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一絮姆、第九天 我趴在偏房一處隱蔽的房頂上張望醉冤。 院中可真熱鬧,春花似錦滚朵、人聲如沸冤灾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匿垄,卻和暖如春移宅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椿疗。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工漏峰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人届榄。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓浅乔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子靖苇,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容

  • 一席噩、引言 float和double類型的主要設計目標是為了科學計算和工程計算。他們執(zhí)行二進制浮點運算贤壁,這是為了在廣...
    AC編程閱讀 223評論 0 1
  • | java.math |--java.math.BigDecimal |--java.math.BigInteg...
    flyrae閱讀 2,767評論 0 2
  • java.lang.Math(final類) Java 語言是徹底地面向對象語言悼枢,哪怕是進行數(shù)學運算也封裝到一個類...
    acc8226閱讀 188評論 0 0
  • 文章首次發(fā)布于https://www.elichen.club 小數(shù)計算丟失精度問題 在計算機中,所有文件都是以二...
    EliCHEN閱讀 1,371評論 1 0
  • 一脾拆、BigDecimal 的介紹 BigDecimal是Java在java.math包中提供的API類馒索,用來對...
    south_zn閱讀 5,656評論 0 0