激情欧美日韩一区二区,浪货撅高贱屁股求主人调教视频,精品无码成人片一区二区98,国产高清av在线播放,色翁荡息又大又硬又粗视频

Java程序中使用浮點(diǎn)數和小數的技巧

時(shí)間:2024-08-09 10:53:52 JAVA認證 我要投稿
  • 相關(guān)推薦

Java程序中使用浮點(diǎn)數和小數的技巧

  許多程序員在其整個(gè)開(kāi)發(fā)生涯中都不曾使用定點(diǎn)或浮點(diǎn)數,可能的例外是,偶爾在計時(shí)測試或基準測試程序中會(huì )用到。下面小編準備了關(guān)于Java程序中使用浮點(diǎn)數和小數的技巧,歡迎大家參考!

  雖然幾乎每種處理器和編程語(yǔ)言都支持浮點(diǎn)運算,但大多數程序員很少注意它。這容易理解 ― 我們中大多數很少需要使用非整數類(lèi)型。除了科學(xué)計算和偶爾的計時(shí)測試或基準測試程序,其它情況下幾乎都用不著(zhù)它。同樣,大多數開(kāi)發(fā)人員也容易忽略 java.math.BigDecimal 所提供的任意精度的小數 ― 大多數應用程序不使用它們。然而,在以整數為主的程序中有時(shí)確實(shí)會(huì )出人意料地需要表示非整型數據。例如,JDBC 使用 BigDecimal 作為 SQL DECIMAL 列的首選互換格式。

  IEEE 浮點(diǎn)

  Java 語(yǔ)言支持兩種基本的浮點(diǎn)類(lèi)型: float 和 double ,以及與它們對應的包裝類(lèi) Float 和 Double .它們都依據 IEEE 754 標準,該標準為 32 位浮點(diǎn)和 64 位雙精度浮點(diǎn)二進(jìn)制小數定義了二進(jìn)制標準。

  IEEE 754 用科學(xué)記數法以底數為 2 的小數來(lái)表示浮點(diǎn)數。IEEE 浮點(diǎn)數用 1 位表示數字的符號,用 8 位來(lái)表示指數,用 23 位來(lái)表示尾數,即小數部分。作為有符號整數的指數可以有正負之分。小數部分用二進(jìn)制(底數 2)小數來(lái)表示,這意味著(zhù)最高位對應著(zhù)值 ?(2 -1),第二位對應著(zhù) ?(2 -2),依此類(lèi)推。對于雙精度浮點(diǎn)數,用 11 位表示指數,52 位表示尾數。IEEE 浮點(diǎn)值的格式如圖 1 所示。

  因為用科學(xué)記數法可以有多種方式來(lái)表示給定數字,所以要規范化浮點(diǎn)數,以便用底數為 2 并且小數點(diǎn)左邊為 1 的小數來(lái)表示,按照需要調節指數就可以得到所需的數字。所以,例如,數 1.25 可以表示為尾數為 1.01,指數為 0: (-1) 0*1.01 2*2 0

  數 10.0 可以表示為尾數為 1.01,指數為 3: (-1) 0*1.01 2*2 3

  特殊數字

  除了編碼所允許的值的標準范圍(對于 float ,從 1.4e-45 到 3.4028235e+38),還有一些表示無(wú)窮大、負無(wú)窮大、 -0 和 NaN(它代表“不是一個(gè)數字”)的特殊值。這些值的存在是為了在出現錯誤條件(譬如算術(shù)溢出,給負數開(kāi)平方根,除以 0 等)下,可以用浮點(diǎn)值集合中的數字來(lái)表示所產(chǎn)生的結果。

  這些特殊的數字有一些不尋常的特征。例如, 0 和 -0 是不同值,但在比較它們是否相等時(shí),被認為是相等的。用一個(gè)非零數去除以無(wú)窮大的數,結果等于 0 .特殊數字 NaN 是無(wú)序的;使用 == 、 < 和 > 運算符將 NaN 與其它浮點(diǎn)值比較時(shí),結果為 false .如果 f 為 NaN,則即使 (f == f) 也會(huì )得到 false .如果想將浮點(diǎn)值與 NaN 進(jìn)行比較,則使用 Float.isNaN() 方法。表 1 顯示了無(wú)窮大和 NaN 的一些屬性。

  表 1. 特殊浮點(diǎn)值的屬性

  表達式 結果

  Math.sqrt(-1.0) -> NaN

  0.0 / 0.0 -> NaN

  1.0 / 0.0 -> 無(wú)窮大

  -1.0 / 0.0 -> 負無(wú)窮大

  NaN + 1.0 -> NaN

  無(wú)窮大 + 1.0 -> 無(wú)窮大

  無(wú)窮大 + 無(wú)窮大 -> 無(wú)窮大

  NaN > 1.0 -> false

  NaN == 1.0 -> false

  NaN < 1.0 -> false

  NaN == NaN -> false

  0.0 == -0.01 -> true

  基本浮點(diǎn)類(lèi)型和包裝類(lèi)浮點(diǎn)有不同的比較行為

  使事情更糟的是,在基本 float 類(lèi)型和包裝類(lèi) Float 之間,用于比較 NaN 和 -0 的規則是不同的。對于 float 值,比較兩個(gè) NaN 值是否相等將會(huì )得到 false ,而使用 Float.equals() 來(lái)比較兩個(gè) NaN Float 對象會(huì )得到 true .造成這種現象的原因是,如果不這樣的話(huà),就不可能將 NaN Float 對象用作 HashMap 中的鍵。類(lèi)似的,雖然 0 和 -0 在表示為浮點(diǎn)值時(shí),被認為是相等的,但使用 Float.compareTo() 來(lái)比較作為 Float 對象的 0 和 -0 時(shí),會(huì )顯示 -0 小于 0 .

  浮點(diǎn)中的危險

  由于無(wú)窮大、NaN 和 0 的特殊行為,當應用浮點(diǎn)數時(shí),可能看似無(wú)害的轉換和優(yōu)化實(shí)際上是不正確的。例如,雖然好象 0.0-f 很明顯等于 -f ,但當 f 為 0 時(shí),這是不正確的。還有其它類(lèi)似的 gotcha,表 2 顯示了其中一些 gotcha.

  表 2. 無(wú)效的浮點(diǎn)假定

  這個(gè)表達式…… 不一定等于…… 當……

  0.0 - f -f f 為 0

  f < g ! (f >= g) f 或 g 為 NaN

  f == f true f 為 NaN

  f + g - g f g 為無(wú)窮大或 NaN舍入誤差

  浮點(diǎn)運算很少是精確的。雖然一些數字(譬如 0.5 )可以精確地表示為二進(jìn)制(底數 2)小數(因為 0.5 等于 2 -1),但其它一些數字(譬如 0.1 )就不能精確的表示。因此,浮點(diǎn)運算可能導致舍入誤差,產(chǎn)生的結果接近 ― 但不等于 ― 您可能希望的結果。例如,下面這個(gè)簡(jiǎn)單的計算將得到 2.600000000000001 ,而不是 2.6 :

  double s=0; for (int i=0; i<26; i++) s += 0.1; System.out.println(s);

  類(lèi)似的, .1*26 相乘所產(chǎn)生的結果不等于 .1 自身加 26 次所得到的結果。當將浮點(diǎn)數強制轉換成整數時(shí),產(chǎn)生的舍入誤差甚至更嚴重,因為強制轉換成整數類(lèi)型會(huì )舍棄非整數部分,甚至對于那些“看上去似乎”應該得到整數值的計算,也存在此類(lèi)問(wèn)題。例如,下面這些語(yǔ)句:

  double d = 29.0 * 0.01; System.out.println(d); System.out.println((int) (d * 100));

  將得到以下輸出:

  0.29 28

  這可能不是您起初所期望的。

  浮點(diǎn)數比較指南

  由于存在 NaN 的不尋常比較行為和在幾乎所有浮點(diǎn)計算中都不可避免地會(huì )出現舍入誤差,解釋浮點(diǎn)值的比較運算符的結果比較麻煩。

  最好完全避免使用浮點(diǎn)數比較。當然,這并不總是可能的,但您應該意識到要限制浮點(diǎn)數比較。如果必須比較浮點(diǎn)數來(lái)看它們是否相等,則應該將它們差的絕對值同一些預先選定的小正數進(jìn)行比較,這樣您所做的就是測試它們是否“足夠接近”。(如果不知道基本的計算范圍,可以使用測試 “abs(a/b - 1) < epsilon”,這種方法比簡(jiǎn)單地比較兩者之差要更準確)。甚至測試看一個(gè)值是比零大還是比零小也存在危險 ―“以為”會(huì )生成比零略大值的計算事實(shí)上可能由于積累的舍入誤差會(huì )生成略微比零小的數字。

  NaN 的無(wú)序性質(zhì)使得在比較浮點(diǎn)數時(shí)更容易發(fā)生錯誤。當比較浮點(diǎn)數時(shí),圍繞無(wú)窮大和 NaN 問(wèn)題,一種避免 gotcha 的經(jīng)驗法則是顯式地測試值的有效性,而不是試圖排除無(wú)效值。在清單 1 中,有兩個(gè)可能的用于特性的 setter 的實(shí)現,該特性只能接受非負數值。第一個(gè)實(shí)現會(huì )接受 NaN,第二個(gè)不會(huì )。第二種形式比較好,因為它顯式地檢測了您認為有效的值的范圍。

  清單 1. 需要非負浮點(diǎn)值的較好辦法和較差辦法

  // Trying to test by exclusion —— this doesn't catch NaN or infinity public void setFoo(float foo) { if (foo < 0) throw new IllegalArgumentException(Float.toString(f)); this.foo = foo; } // Testing by inclusion —— this does catch NaN public void setFoo(float foo) { if (foo >= 0 && foo < Float.INFINITY) this.foo = foo; else throw new IllegalArgumentException(Float.toString(f)); }不要用浮點(diǎn)值表示精確值

  一些非整數值(如幾美元和幾美分這樣的小數)需要很精確。浮點(diǎn)數不是精確值,所以使用它們會(huì )導致舍入誤差。因此,使用浮點(diǎn)數來(lái)試圖表示象貨幣量這樣的精確數量不是一個(gè)好的想法。使用浮點(diǎn)數來(lái)進(jìn)行美元和美分計算會(huì )得到災難性的后果。浮點(diǎn)數最好用來(lái)表示象測量值這類(lèi)數值,這類(lèi)值從一開(kāi)始就不怎么精確。

  用于較小數的 BigDecimal

  從 JDK 1.3 起,Java 開(kāi)發(fā)人員就有了另一種數值表示法來(lái)表示非整數: BigDecimal . BigDecimal 是標準的類(lèi),在編譯器中不需要特殊支持,它可以表示任意精度的小數,并對它們進(jìn)行計算。在內部,可以用任意精度任何范圍的值和一個(gè)換算因子來(lái)表示 BigDecimal ,換算因子表示左移小數點(diǎn)多少位,從而得到所期望范圍內的值。因此,用 BigDecimal 表示的數的形式為 unscaledValue*10 -scale .

  用于加、減、乘和除的方法給 BigDecimal 值提供了算術(shù)運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個(gè)都會(huì )產(chǎn)生新的 BigDecimal 對象。因此,因為創(chuàng )建對象的開(kāi)銷(xiāo), BigDecimal 不適合于大量的數學(xué)計算,但設計它的目的是用來(lái)精確地表示小數。如果您正在尋找一種能精確表示如貨幣量這樣的數值,則 BigDecimal 可以很好地勝任該任務(wù)。

  所有的 equals 方法都不能真正測試相等

  如浮點(diǎn)類(lèi)型一樣, BigDecimal 也有一些令人奇怪的行為。尤其在使用 equals() 方法來(lái)檢測數值之間是否相等時(shí)要小心。 equals() 方法認為,兩個(gè)表示同一個(gè)數但換算值不同(例如, 100.00 和 100.000 )的 BigDecimal 值是不相等的。然而, compareTo() 方法會(huì )認為這兩個(gè)數是相等的,所以在從數值上比較兩個(gè) BigDecimal 值時(shí),應該使用 compareTo() 而不是 equals() .

  另外還有一些情形,任意精度的小數運算仍不能表示精確結果。例如, 1 除以 9 會(huì )產(chǎn)生無(wú)限循環(huán)的小數 .111111…… .出于這個(gè)原因,在進(jìn)行除法運算時(shí), BigDecimal 可以讓您顯式地控制舍入。 movePointLeft() 方法支持 10 的冪次方的精確除法。

  使用 BigDecimal 作為互換類(lèi)型

  SQL-92 包括 DECIMAL 數據類(lèi)型,它是用于表示定點(diǎn)小數的精確數字類(lèi)型,它可以對小數進(jìn)行基本的算術(shù)運算。一些 SQL 語(yǔ)言喜歡稱(chēng)此類(lèi)型為 NUMERIC 類(lèi)型,其它一些 SQL 語(yǔ)言則引入了 MONEY 數據類(lèi)型,MONEY 數據類(lèi)型被定義為小數點(diǎn)右側帶有兩位的小數。

  如果希望將數字存儲到數據庫中的 DECIMAL 字段,或從 DECIMAL 字段檢索值,則如何確保精確地轉換該數字?您可能不希望使用由 JDBC PreparedStatement 和 ResultSet 類(lèi)所提供的 setFloat() 和 getFloat() 方法,因為浮點(diǎn)數與小數之間的轉換可能會(huì )喪失精確性。相反,請使用 PreparedStatement 和 ResultSet 的 setBigDecimal() 及 getBigDecimal() 方法。

  對于 BigDecimal ,有幾個(gè)可用的構造函數。其中一個(gè)構造函數以雙精度浮點(diǎn)數作為輸入,另一個(gè)以整數和換算因子作為輸入,還有一個(gè)以小數的 String 表示作為輸入。要小心使用 BigDecimal(double) 構造函數,因為如果不了解它,會(huì )在計算過(guò)程中產(chǎn)生舍入誤差。請使用基于整數或 String 的構造函數。

  構造 BigDecimal 數

  對于 BigDecimal ,有幾個(gè)可用的構造函數。其中一個(gè)構造函數以雙精度浮點(diǎn)數作為輸入,另一個(gè)以整數和換算因子作為輸入,還有一個(gè)以小數的 String 表示作為輸入。要小心使用 BigDecimal(double) 構造函數,因為如果不了解它,會(huì )在計算過(guò)程中產(chǎn)生舍入誤差。請使用基于整數或 String 的構造函數。

  如果使用 BigDecimal(double) 構造函數不恰當,在傳遞給 JDBC setBigDecimal() 方法時(shí),會(huì )造成似乎很奇怪的 JDBC 驅動(dòng)程序中的異常。例如,考慮以下 JDBC 代碼,該代碼希望將數字 0.01 存儲到小數字段:

  PreparedStatement ps = connection.prepareStatement("INSERT INTO Foo SET name=?, value=?"); ps.setString(1, "penny"); ps.setBigDecimal(2, new BigDecimal(0.01)); ps.executeUpdate();

  在執行這段似乎無(wú)害的代碼時(shí)會(huì )拋出一些令人迷惑不解的異常(這取決于具體的 JDBC 驅動(dòng)程序),因為 0.01 的雙精度近似值會(huì )導致大的換算值,這可能會(huì )使 JDBC 驅動(dòng)程序或數據庫感到迷惑。JDBC 驅動(dòng)程序會(huì )產(chǎn)生異常,但可能不會(huì )說(shuō)明代碼實(shí)際上錯在哪里,除非意識到二進(jìn)制浮點(diǎn)數的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 構造 BigDecimal 來(lái)避免這類(lèi)問(wèn)題,因為這兩種方法都可以精確地表示小數。

【Java程序中使用浮點(diǎn)數和小數的技巧】相關(guān)文章:

java程序中如何調用linux命令03-29

使用毛筆的方法和技巧08-30

Java程序員必知調試技巧匯總03-07

Java中類(lèi)的設計技巧有哪些03-04

戶(hù)外睡袋的使用技巧和講究03-10

戶(hù)外背包的使用和選購技巧03-10

java中continue跳轉語(yǔ)句使用方法03-29

java中File類(lèi)有哪些使用方法03-14

如何編譯java程序03-05

激情欧美日韩一区二区,浪货撅高贱屁股求主人调教视频,精品无码成人片一区二区98,国产高清av在线播放,色翁荡息又大又硬又粗视频