电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

詳解java中BigDecimal精度問題

瀏覽:3日期:2022-08-12 18:33:16
一、背景

在實(shí)際開發(fā)中,對于 不需要任何準(zhǔn)確計(jì)算精度的屬性可以直接使用float或double,但是如果需要精確計(jì)算結(jié)果,則必須使用BigDecimal,例如價(jià)格、質(zhì)量。

為什么這么說,主要有兩點(diǎn)

1、double計(jì)算會(huì)有精度丟失問題

2、在除法運(yùn)算時(shí),BigDecimal提供了豐富的取舍規(guī)則。(double雖然可以通過NumberFormat進(jìn)行四舍五入,但是NumberFormat是線程不安全的)

對于精度問題我們可以看下實(shí)際的例子

public static void main(String[] args) { //正常 3.3 System.out.println('加法結(jié)果:'+(1.1+2.2)); //正常 -7.9 System.out.println('減法結(jié)果:'+(2.2-10.1)); //正常 2.42 System.out.println('乘法結(jié)果:'+(1.1*2.2)); //正常 0.44 System.out.println('除法結(jié)果:'+(4.4/10));}

實(shí)際控制臺輸出

詳解java中BigDecimal精度問題

為什么會(huì)這樣

在于我們的計(jì)算機(jī)是二進(jìn)制的。浮點(diǎn)數(shù)沒有辦法是用二進(jìn)制進(jìn)行精確表示。我們的CPU表示浮點(diǎn)數(shù)由兩個(gè)部分組成:指數(shù)和尾數(shù),這樣的表示方法一般都會(huì)

失去一定的精確度,有些浮點(diǎn)數(shù)運(yùn)算也會(huì)產(chǎn)生一定的誤差。如:2.4的二進(jìn)制表示并非就是精確的2.4。反而最為接近的二進(jìn)制表示是 2.3999999999999999。

浮點(diǎn)數(shù)的值實(shí)際上是由一個(gè)特定的數(shù)學(xué)公式計(jì)算得到的。

二、BigDecimal構(gòu)造函數(shù)1、四種構(gòu)造函數(shù)

BigDecimal(int) //創(chuàng)建一個(gè)具有參數(shù)所指定整數(shù)值的對象。BigDecimal(double) //創(chuàng)建一個(gè)具有參數(shù)所指定雙精度值的對象。BigDecimal(long) //創(chuàng)建一個(gè)具有參數(shù)所指定長整數(shù)值的對象。BigDecimal(String) //創(chuàng)建一個(gè)具有參數(shù)所指定以字符串表示的數(shù)值的對象。

這幾個(gè)都是常用的構(gòu)造器,他們返回的對象都是BigDecimal對象。換而言之,將BigDecimal對象轉(zhuǎn)換為其他類型的對象,我們通過以下幾種。

toString() //將BigDecimal對象的數(shù)值轉(zhuǎn)換成字符串。doubleValue() //將BigDecimal對象中的值以雙精度數(shù)返回。floatValue()//將BigDecimal對象中的值以單精度數(shù)返回。longValue() //將BigDecimal對象中的值以長整數(shù)返回。intValue() //將BigDecimal對象中的值以整數(shù)返回。

這里需要非常注意BigDecimal(double)的構(gòu)造函數(shù),也是會(huì)存在精度丟失的問題,其它的不會(huì),這里也可以舉例說明

public static void main(String[] args) { BigDecimal intDecimal = new BigDecimal(10); BigDecimal doubleDecimal = new BigDecimal(4.3); BigDecimal longDecimal = new BigDecimal(10L); BigDecimal stringDecimal = new BigDecimal('4.3'); System.out.println('intDecimal=' + intDecimal); System.out.println('doubleDecimal=' + doubleDecimal); System.out.println('longDecimal=' + longDecimal); System.out.println('stringDecimal=' + stringDecimal);}

控制臺實(shí)際輸出

詳解java中BigDecimal精度問題

從圖中很明顯可以看出,對于double的構(gòu)造函數(shù)是會(huì)存在精度丟失的可能的。

2、為什么會(huì)出現(xiàn)這種情況

這個(gè)在new BigDecimal(double)類型的構(gòu)造函數(shù)上的注解有解釋說明。

這個(gè)構(gòu)造函數(shù)的結(jié)果可能有些不可預(yù)測。 可以假設(shè)在Java中寫入new BigDecimal(0.1)創(chuàng)建一個(gè)BigDecimal ,它完全等于0.1(非標(biāo)尺值為1,比例為1),但實(shí)際上等于

0.1000000000000000055511151231257827021181583404541015625。 這是因?yàn)?.1不能像double (或者作為任何有限長度的二進(jìn)制分?jǐn)?shù))精確地表示。

因此,正在被傳遞給構(gòu)造的值不是正好等于0.1。

3、如何解決

有兩種常用的解決辦法。

1、是將double 通過Double.toString(double)先轉(zhuǎn)為String,然后放入BigDecimal的String構(gòu)造函數(shù)中。

2、不通過BigDecimal的構(gòu)造函數(shù),而是通過它的靜態(tài)方法BigDecimal.valueOf(double),也同樣不會(huì)丟失精度。

示例

public static void main(String[] args) { String string = Double.toString(4.3); BigDecimal stringBigDecimal = new BigDecimal(string); BigDecimal bigDecimal = BigDecimal.valueOf(4.3); System.out.println('stringBigDecimal = ' + stringBigDecimal); System.out.println('bigDecimal = ' + bigDecimal);}

運(yùn)行結(jié)果

詳解java中BigDecimal精度問題

這樣也能保證,對與double而言,轉(zhuǎn)BigDecimal不會(huì)出現(xiàn)精度丟失的情況。

三、常用方法1、常用方法

示例

public static void main(String[] args) { BigDecimal a = new BigDecimal('4.5'); BigDecimal b = new BigDecimal('1.5'); BigDecimal c = new BigDecimal('-10.5'); BigDecimal add_result = a.add(b); BigDecimal subtract_result = a.subtract(b); BigDecimal multiply_result = a.multiply(b); BigDecimal divide_result = a.divide(b); BigDecimal remainder_result = a.remainder(b); BigDecimal max_result = a.max(b); BigDecimal min_result = a.min(b); BigDecimal abs_result = c.abs(); BigDecimal negate_result = a.negate(); System.out.println('4.5+1.5=' + add_result); System.out.println('4.5-1.5=' + subtract_result); System.out.println('4.5*1.5=' + multiply_result); System.out.println('4.5/1.5=' + divide_result); System.out.println('4.5/1.5余數(shù)=' + remainder_result); System.out.println('4.5和1.5最大數(shù)=' + max_result); System.out.println('4.5和1.5最小數(shù)=' + min_result); System.out.println('-10.5的絕對值=' + abs_result); System.out.println('4.5的相反數(shù)=' + negate_result);}

4.5+1.5=6.0

4.5-1.5=3.0

4.5*1.5=6.75

4.5/1.5=3

4.5/1.5余數(shù)=0.0

4.5和1.5最大數(shù)=4.5

4.5和1.5最小數(shù)=1.5

-10.5的絕對值=10.5

4.5的相反數(shù)=-4.5

這里把除法單獨(dú)再講一下,因?yàn)槌ú僮鞯臅r(shí)候會(huì)有除不盡的情況,,比如 3,5/3,這時(shí)會(huì)報(bào)錯(cuò)java.lang.ArithmeticException: Non-terminating decimal expansion;

no exact representable decimal result。所以這里要考慮除不盡的情況下,保留幾位小數(shù),取舍規(guī)則。(除法如果可能存在除不進(jìn),那就用下面方法)

BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 第一參數(shù)表示除數(shù),第二個(gè)參數(shù)表示小數(shù)點(diǎn)后保留位數(shù),第三個(gè)參數(shù)表示取舍規(guī)則。2、取舍規(guī)則

ROUND_UP //不管保留數(shù)字后面是大是小(0除外)都會(huì)進(jìn)1ROUND_DOWN//保留設(shè)置數(shù)字,后面所有直接去除ROUND_HALF_UP //常用的四舍五入 ROUND_HALF_DOWN //五舍六入ROUND_CEILING //向正無窮方向舍入ROUND_FLOOR //向負(fù)無窮方向舍入ROUND_HALF_EVEN //向(距離)最近的一邊舍入,除非兩邊(的距離)是相等,如果是這樣,如果保留位數(shù)是奇數(shù),使用ROUND_HALF_UP,如果是偶數(shù),使用ROUND_HALF_DOWNROUND_UNNECESSARY //計(jì)算結(jié)果是精確的,不需要舍入模式

注意 我們最常用的應(yīng)該是 ROUND_HALF_UP(四舍五入)

這里舉幾個(gè)常用的取舍規(guī)則

public static void main(String[] args) { BigDecimal a = new BigDecimal('1.15'); BigDecimal b = new BigDecimal('1'); //不管保留數(shù)字后面是大是小(0除外)都會(huì)進(jìn)1 所以這里輸出為1.2 BigDecimal divide_1 = a.divide(b,1,BigDecimal.ROUND_UP); //保留設(shè)置數(shù)字,后面所有直接去除 所以這里輸出為1.1 BigDecimal divide_2 = a.divide(b,1,BigDecimal.ROUND_DOWN); //常用的四舍五入 所以這里輸出1.2 BigDecimal divide_3 = a.divide(b,1,BigDecimal.ROUND_HALF_UP); //這個(gè)可以理解成五舍六入 所以這里輸出1.1 BigDecimal divide_4 = a.divide(b,1,BigDecimal.ROUND_HALF_DOWN); //這里將1.15改成1.16 BigDecimal c = new BigDecimal('1.16'); //那么這里就符合六入了 所以輸出變?yōu)?.2 BigDecimal divide_5 = c.divide(b,1,BigDecimal.ROUND_HALF_DOWN); System.out.println('divide_1 = ' + divide_1); System.out.println('divide_2 = ' + divide_2); System.out.println('divide_3 = ' + divide_3); System.out.println('divide_4 = ' + divide_4); System.out.println('divide_5 = ' + divide_5);}

運(yùn)行結(jié)果

divide_1 = 1.2

divide_2 = 1.1

divide_3 = 1.2

divide_4 = 1.1

divide_5 = 1.2

四、格式化

由于NumberFormat類的format()方法可以使用BigDecimal對象作為其參數(shù),可以利用BigDecimal對超出16位有效數(shù)字的貨幣值,百分值,以及一般數(shù)值進(jìn)行格式化控制。

以利用BigDecimal對貨幣和百分比格式化為例。首先,創(chuàng)建BigDecimal對象,進(jìn)行BigDecimal的算術(shù)運(yùn)算后,分別建立對貨幣和百分比格式化的引用,最后利用

BigDecimal對象作為format()方法的參數(shù),輸出其格式化的貨幣值和百分比。

示例

public static void main(String[] args) { //建立貨幣格式化引用 NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立百分比格式化引用 NumberFormat percent = NumberFormat.getPercentInstance(); //百分比小數(shù)點(diǎn)最多3位 percent.setMaximumFractionDigits(3); //取整 NumberFormat integerInstance = NumberFormat.getIntegerInstance(); ////金額 BigDecimal loanAmount = new BigDecimal('188.555'); ////利率 BigDecimal interestRate = new BigDecimal('0.018555555'); //沒有指定保留位數(shù)的情況下 默認(rèn)保留2位 System.out.println('金額: ' + currency.format(loanAmount)); //貨幣(百分比)格式化 指定默認(rèn)的取舍規(guī)則是四舍五入 System.out.println('利率: ' + percent.format(interestRate)); //取整還有點(diǎn)不一樣 188.555取整為189, 188.51也是189 但是189.5確實(shí)188,所以它不是真正意義上的四舍五入 System.out.println('取整: ' + integerInstance.format(loanAmount));}

運(yùn)行結(jié)果

金額: ¥188.56利率: 1.856%取整: 189

這里有幾點(diǎn)在說明下

1、格式化的時(shí)候沒有指定保留位數(shù)的情況下 默認(rèn)保留2位。

2、貨幣(百分比)格式化 指定默認(rèn)的取舍規(guī)則是四舍五入。

3、取整還有點(diǎn)不一樣 188.555取整為189, 188.51也是189 但是189.5確實(shí)188,所以它不是真正意義上的四舍五入。

以上就是詳解java中BigDecimal精度問題的詳細(xì)內(nèi)容,更多關(guān)于java的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 上海地磅秤|电子地上衡|防爆地磅_上海地磅秤厂家–越衡称重 | 厂房出租_厂房出售_产业园区招商_工业地产 - 中工招商网 | 皮带输送机-大倾角皮带输送机-皮带输送机厂家-河南坤威机械 | 新能源汽车电池软连接,铜铝复合膜柔性连接,电力母排-容发智能科技(无锡)有限公司 | 东莞螺杆空压机_永磁变频空压机_节能空压机_空压机工厂批发_深圳螺杆空压机_广州螺杆空压机_东莞空压机_空压机批发_东莞空压机工厂批发_东莞市文颖设备科技有限公司 | 泥沙分离_泥沙分离设备_泥砂分离机_洛阳隆中重工机械有限公司 | 东莞注册公司-代办营业执照-东莞公司注册代理记账-极刻财税 | 铁艺,仿竹,竹节,护栏,围栏,篱笆,栅栏,栏杆,护栏网,网围栏,厂家 - 河北稳重金属丝网制品有限公司 山东太阳能路灯厂家-庭院灯生产厂家-济南晟启灯饰有限公司 | 黄石东方妇产医院_黄石妇科医院哪家好_黄石无痛人流医院 | 千淘酒店差旅平台-中国第一家针对TMC行业的酒店资源供应平台 | 顺辉瓷砖-大国品牌-中国顺辉| 窖井盖锯圆机_锯圆机金刚石锯片-无锡茂达金刚石有限公司 | 两头忙,井下装载机,伸缩臂装载机,30装载机/铲车,50装载机/铲车厂家_价格-莱州巨浪机械有限公司 | 德国EA可编程直流电源_电子负载,中国台湾固纬直流电源_交流电源-苏州展文电子科技有限公司 | 葡萄酒灌装机-食用油灌装机-液体肥灌装设备厂家_青州惠联灌装机械 | 小港信息港-鹤壁信息港 鹤壁老百姓便民生活信息网站 | 福尔卡(北京)新型材料技术股份有限公司| 数码听觉统合训练系统-儿童感觉-早期言语评估与训练系统-北京鑫泰盛世科技发展有限公司 | 礼仪庆典公司,礼仪策划公司,庆典公司,演出公司,演艺公司,年会酒会,生日寿宴,动工仪式,开工仪式,奠基典礼,商务会议,竣工落成,乔迁揭牌,签约启动-东莞市开门红文化传媒有限公司 | 合肥防火门窗/隔断_合肥防火卷帘门厂家_安徽耐火窗_良万消防设备有限公司 | 哈希余氯测定仪,分光光度计,ph在线监测仪,浊度测定仪,试剂-上海京灿精密机械有限公司 | 大立教育官网-一级建造师培训-二级建造师培训-造价工程师-安全工程师-监理工程师考试培训 | 户外环保不锈钢垃圾桶_标识标牌制作_园林公园椅厂家_花箱定制-北京汇众环艺 | pos机办理,智能/扫码/二维码/微信支付宝pos机-北京万汇通宝商贸有限公司 | 重庆私家花园设计-别墅花园-庭院-景观设计-重庆彩木园林建设有限公司 | 卓能JOINTLEAN端子连接器厂家-专业提供PCB接线端子|轨道式端子|重载连接器|欧式连接器等电气连接产品和服务 | 电磁铁_小型推拉电磁铁_电磁阀厂家-深圳市宗泰电机有限公司 | 衢州装饰公司|装潢公司|办公楼装修|排屋装修|别墅装修-衢州佳盛装饰 | 吊篮式|移动式冷热冲击试验箱-二槽冷热冲击试验箱-广东科宝 | 诗词大全-古诗名句 - 古诗词赏析 | 橡胶接头|可曲挠橡胶接头|橡胶软接头安装使用教程-上海松夏官方网站 | 低温等离子清洗机(双气路进口)-嘉润万丰 | 恒压供水控制柜|无负压|一体化泵站控制柜|PLC远程调试|MCGS触摸屏|自动控制方案-联致自控设备 | 耐磨陶瓷管道_除渣器厂家-淄博浩瀚陶瓷科技有限公司 | 桑茶-七彩贝壳桑叶茶 长寿茶 | 广东高华家具-公寓床|学生宿舍双层铁床厂家【质保十年】 | 快速门厂家-快速卷帘门-工业快速门-硬质快速门-西朗门业 | 南京欧陆电气股份有限公司-风力发电机官网 | 杭州顺源过滤机械有限公司官网-压滤机_板框压滤机_厢式隔膜压滤机厂家 | 一礼通 (www.yilitong.com)-企业礼品解决方案一站式服务平台 | 周易算网-八字测算网 - 周易算网-宝宝起名取名测名字周易八字测算网 |