第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Java:為什么在現實世界中我們應該使用BigDecimal而不是Double?

Java:為什么在現實世界中我們應該使用BigDecimal而不是Double?

手掌心 2019-10-28 14:28:51
建議在處理現實世界的貨幣值時,使用BigDecimal而不是Double。但是我沒有令人信服的解釋,只是“通常是這樣做的”。您能談談這個問題嗎?
查看完整描述

3 回答

?
慕桂英3389331

TA貢獻2036條經驗 獲得超8個贊

我認為這描述了您的問題的解決方案:Java陷阱:大十進制以及此處出現雙精度問題


來自原始博客,該博客現在似乎已關閉。


Java陷阱:雙倍


學徒程序員在走軟件開發(fā)道路時面臨許多陷阱。本文通過一系列實際示例說明了使用Java的簡單類型double和float的主要陷阱。但是請注意,要在數值計算中完全包含精度,您需要一本(或兩本)關于該主題的教科書。因此,我們只能從頭開始討論話題。話雖如此,此處傳達的知識應為您提供發(fā)現或識別代碼中的錯誤所需的基本知識。我認為這是任何專業(yè)軟件開發(fā)人員都應該了解的知識。


小數是近似值


雖然可以使用8位精確地描述0至255之間的所有自然數,但是描述所有0.0至255.0之間的實數則需要無限數量的位。首先,在該范圍內(甚至在0.0-0.1范圍內)有無數個要描述的數字,其次,某些無理數根本無法用數字描述。例如e和π。換句話說,數字2和0.2在計算機中的顯示方式大不相同。


整數由代表值2n的位表示,其中n是位的位置。因此,值6被表示為23 * 0 + 22 * 1 + 21 * 1 + 20 * 0與位序列0110相對應。另一方面,小數由代表2-n(即分數)1/2, 1/4, 1/8,...的位描述,數字0.75對應于2-1 * 1 + 2-2 * 1 + 2-3 * 0 + 2-4 * 0產生位序列1100 (1/2 + 1/4)。


有了這些知識,我們可以制定以下經驗法則:任何十進制數字都由一個近似值表示。


讓我們通過執(zhí)行一系列瑣碎的乘法來研究其實際后果。


System.out.println( 0.2 + 0.2 + 0.2 + 0.2 + 0.2 );

1.0

打印1.0。盡管這確實是正確的,但它可能給我們一種錯誤的安全感。巧合的是,0.2是Java能夠正確表示的少數幾個值之一。讓我們通過另一個瑣碎的算術問題再次挑戰(zhàn)Java,將數字0.1加十倍。


System.out.println( 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f );

System.out.println( 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d + 0.1d );


1.0000001

0.9999999999999999

根據Joseph D. Darcy博客的幻燈片,這兩個計算的總和分別為0.100000001490116119384765625和0.1000000000000000055511151231...。這些結果對于有限的一組數字是正確的。float的精度為8個前導數字,而double的精度為17個前導數字?,F在,如果預期結果1.0與屏幕上打印的結果之間的概念上的不匹配不足以使您的警鐘響起,那么請注意Mr.的數字。達西的幻燈片似乎與印刷的數字不符!那是另一個陷阱。關于此的更多信息。


在看似最簡單的情況下意識到了錯誤的計算之后,就可以合理地考慮印象可能會出現多快了。讓我們將問題簡化為僅添加三個數字。


System.out.println( 0.3 == 0.1d + 0.1d + 0.1d );

false

令人震驚的是,不精確性已經在三個附加項中開始發(fā)揮作用!


加倍溢出


與Java中的其他任何簡單類型一樣,雙精度數由一組有限的位表示。因此,將一個值加或乘以一個雙可以產生令人驚訝的結果。誠然,數字必須很大才能溢出,但是它確實發(fā)生了。讓我們嘗試相乘然后除以大數。數學直覺說結果是原始數字。在Java中,我們可能會得到不同的結果。


double big = 1.0e307 * 2000 / 2000;

System.out.println( big == 1.0e307 );

false

這里的問題是先將big乘以并溢出,然后再將溢出的數相除。更糟糕的是,不會向程序員發(fā)出異?;蚱渌愋偷木??;旧希@使表達式x * y完全不可靠,因為在通常情況下,對于由x,y表示的所有double值均不作任何表示或保證。


大大小小的不是朋友!


勞雷爾和哈代經常在很多事情上意見分歧。同樣在計算中,大大小小的都不是朋友。使用固定位數表示數字的結果是,在相同的計算中對非常大和非常小的數字進行運算將無法按預期工作。讓我們嘗試將小的東西添加到大的東西中。


System.out.println( 1234.0d + 1.0e-13d == 1234.0d );

true

加法沒有效果!這與任何(理智的)加法直覺相矛盾,即直覺上給定兩個數d和f,則d + f> d。


小數不能直接比較


到目前為止,我們所學到的是,我們必須拋棄在數學課和整數編程中獲得的所有直覺。請謹慎使用小數。例如,該語句for(double d = 0.1; d != 0.3; d += 0.1)實際上是一個偽裝的永無止境的循環(huán)!錯誤是直接將十進制數字相互比較。您應遵循以下準則。


避免兩個小數之間的相等性測試。不要在容忍度可以是常量的情況下if(a == b) {..}使用if(Math.abs(a-b) < tolerance) {..},例如,公共靜態(tài)最終雙容忍度= 0.01請考慮使用運算符<,>作為替代,因為它們可以更自然地描述您想要表達的內容。例如,我更喜歡表單 for(double d = 0; d <= 10.0; d+= 0.1)而不是笨拙的 for(double d = 0; Math.abs(10.0-d) < tolerance; d+= 0.1) 兩種表單,但都視情況而定:在單元測試中,我更傾向于表達,assertEquals(2.5, d, tolerance)而assertTrue(d > 2.5)不是說不僅第一個表單讀得更好,而且通常是您想要進行的檢查正在做(即d不太大)。


所見即所得-所見即所得


所見即所得是通常在圖形用戶界面應用程序中使用的表達式。它的意思是“所見即所得”,用于計算以描述一個系統,在該系統中,編輯期間顯示的內容看起來與最終輸出非常相似,該最終輸出可能是打印的文檔,網頁等。這個短語最初是一個流行的流行短語,由Flip Wilson的扮靚人物“ Geraldine”提出,她經常說“您看到的就是您所得到的”,以借口她古怪的行為(來自維基百科)。


程序員經常陷入的另一個嚴重陷阱,就是認為十進制數是所見即所得。必須意識到,在打印或寫入十進制數時,不是打印/寫入的近似值。換一種說法,Java在幕后做了很多近似,并且不斷地試圖使您不了解它。只有一個問題。您需要了解這些近似值,否則您的代碼中可能會遇到各種各樣的神秘錯誤。


但是,只要有一些獨創(chuàng)性,我們就可以調查幕后的真實情況。到現在為止,我們知道數字0.1已被近似表示。


System.out.println( 0.1d );

0.1

我們知道0.1不是0.1,但屏幕上仍打印了0.1。結論:Java是所見即所得!


為了多樣化,讓我們選擇另一個看起來很純真的數字,例如2.3。像0.1一樣,2.3是一個近似值。毫不奇怪,當打印數字時,Java隱藏了近似值。


System.out.println( 2.3d );

2.3

要研究2.3的內部近似值是多少,我們可以將該數字與附近的其他數字進行比較。


double d1 = 2.2999999999999996d;

double d2 = 2.2999999999999997d;

System.out.println( d1 + " " + (2.3d == d1) );

System.out.println( d2 + " " + (2.3d == d2) );

2.2999999999999994 false

2.3 true

因此2.2999999999999997等于2.3等于2.3!還要注意,由于近似值,樞軸點位于..99997處,而不是..99995處,通常在數學上四舍五入。掌握近似值的另一種方法是調用BigDecimal的服務。


System.out.println( new BigDecimal(2.3d) );

2.29999999999999982236431605997495353221893310546875

現在,不要以為您可以跳船而僅使用BigDecimal來得意。BigDecimal此處記錄了自己的陷阱集合。


沒有什么是容易的,而且?guī)缀鯖]有免費的東西。而“自然”,浮動和雙打則在打印/書寫時產生不同的結果。


System.out.println( Float.toString(0.1f) );

System.out.println( Double.toString(0.1f) );

System.out.println( Double.toString(0.1d) );

0.1

0.10000000149011612

0.1

根據約瑟夫·D·達西(Joseph D. Darcy)博客的幻燈片,浮點近似具有24個有效位,而雙近似具有53個有效位。士氣是為了保留值,必須以相同格式讀取和寫入十進制數字。


被0除


許多開發(fā)人員從經驗中知道,將數字除以零會導致應用程序突然終止。在int上進行操作時,發(fā)現了類似的行為,但很令人驚訝,在double上進行操作時,卻發(fā)現了Java。除零外,任何數字除以零分別得出∞或-∞。將零除以零會產生特殊的NaN,即非數字值。


System.out.println(22.0 / 0.0);

System.out.println(-13.0 / 0.0);

System.out.println(0.0 / 0.0);

Infinity

-Infinity

NaN

將正數與負數相除會產生負數的結果,而將負數與負數相除會產生正數的結果。由于可以用零除,因此根據將數字除以0.0還是-0.0會得到不同的結果。對,是真的!Java為負零!但是不要被愚弄,兩個零值相等,如下所示。


System.out.println(22.0 / 0.0);

System.out.println(22.0 / -0.0);

System.out.println(0.0 == -0.0);

Infinity

-Infinity

true

無限很奇怪


在數學世界中,無窮大是我很難理解的概念。例如,當一個無窮大比另一個無窮大時,我從未獲得直覺。當然,Z> N,所有有理數的集合都比自然數的集合無限大,但這大約是我在這方面的直覺極限!


幸運的是,Java中的無窮與數學世界中的無窮差不多。您可以對無窮大的值執(zhí)行通常的可疑行為(+,-,*,/,但不能將無窮大應用于無窮大。


double infinity = 1.0 / 0.0;

System.out.println(infinity + 1);

System.out.println(infinity / 1e300);

System.out.println(infinity / infinity);

System.out.println(infinity - infinity);

Infinity

Infinity

NaN

NaN

這里的主要問題是返回NaN值而沒有任何警告。因此,如果您愚蠢地調查某個特定的雙精度是偶數還是奇數,您確實會陷入困境。也許運行時異常會更合適?


double d = 2.0, d2 = d - 2.0;

System.out.println("even: " + (d % 2 == 0) + " odd: " + (d % 2 == 1));

d = d / d2;

System.out.println("even: " + (d % 2 == 0) + " odd: " + (d % 2 == 1));

even: true odd: false

even: false odd: false

突然,您的變量既不是奇數也不是偶數!NaN甚至比Infinity還要奇怪。無限值與double的最大值不同,而NaN與無限值又不同。


double nan = 0.0 / 0.0, infinity = 1.0 / 0.0;

System.out.println( Double.MAX_VALUE != infinity );

System.out.println( Double.MAX_VALUE != nan );

System.out.println( infinity         != nan );

true

true

true

通常,當雙精度數已獲取值NaN時,對其進行任何運算都會得到NaN。


System.out.println( nan + 1.0 );

NaN

結論


小數是近似值,而不是您分配的值。在數學世界中獲得的任何直覺都不再適用。期望a+b = a和a != a/3 + a/3 + a/3

避免使用==,與一些容差進行比較或使用> =或<=運算符

Java是所見即所得!永遠不要相信您打印/寫入的值是近似值,因此始終以相同格式讀取/寫入十進制數。

注意不要使雙精度對象溢出,也不要使雙精度對象變?yōu)椤繧nfinity或NaN。無論哪種情況,您的計算結果都可能不符合您的預期。您可能會發(fā)現,始終在返回方法值之前先檢查這些值是一個好主意。


查看完整回答
反對 回復 2019-10-28
?
森欄

TA貢獻1810條經驗 獲得超5個贊

盡管BigDecimal可以存儲比double精度更高的精度,但是通常不需要這樣做。使用它的真正原因是因為它可以清楚地說明如何執(zhí)行舍入,包括許多不同的舍入策略。在大多數情況下,您可以用兩倍來獲得相同的結果,但是除非您知道所需的技術,否則BigDecimal是解決這些問題的方法。


一個常見的例子是金錢。即使資金不足以在99%的用例中都需要BigDecimal的精度,但通常仍認為使用BigDecimal是最佳實踐,因為舍入控制在軟件中,這避免了開發(fā)人員可能做出的風險。處理舍入時出錯。即使您有信心可以使用進行舍入,double我還是建議您使用輔助方法來執(zhí)行經過徹底測試的舍入。


查看完整回答
反對 回復 2019-10-28
  • 3 回答
  • 0 關注
  • 1613 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號