国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Java理論與實踐:您的小數點在哪?

2019-11-18 13:25:45
字體:
來源:轉載
供稿:網友

  java理論與實踐:您的小數點在哪?
          ——使用浮點數和小數中的技巧和陷阱
  作者:Brian Goetz 本文選自:IBM DW中國網站 2003年04月15日
    許多程序員在其整個開發生涯中都不曾使用定點或浮點數,可能的例外是,偶然在計時測試或基準測試程序中會用到。Java 語言和類庫支持兩類非整數類型 — IEEE 754 浮點(float 和 double,包裝類(wrapper class)為 Float 和 Double),以及任意精度的小數(java.math.BigDecimal)。在本月的 Java 理論和實踐中,Brian Goetz 探討了在 Java 程序中使用非整數類型時一些常碰到的陷阱和“gotcha”。
    雖然幾乎每種處理器和編程語言都支持浮點運算,但大多數程序員很少注重它。這輕易理解 — 我們中大多數很少需要使用非整數類型。除了科學計算和偶然的計時測試或基準測試程序,其它情況下幾乎都用不著它。同樣,大多數開發人員也輕易忽略 java.math.BigDecimal 所提供的任意精度的小數 — 大多數應用程序不使用它們。然而,在以整數為主的程序中有時確實會出人意料地需要表示非整型數據。例如,JDBC 使用 BigDecimal 作為 SQL DECIMAL 列的首選互換格式。
  IEEE 浮點
   Java 語言支持兩種基本的浮點類型:float 和 double,以及與它們對應的包裝類 Float 和 Double。它們都依據 IEEE 754 標準,該標準為 32 位浮點和 64 位雙精度浮點二進制小數定義了二進制標準。
    IEEE 754 用科學記數法以底數為 2 的小數來表示浮點數。IEEE 浮點數用 1 位表示數字的符號,用 8 位來表示指數,用 23 位來表示尾數,即小數部分。作為有符號整數的指數可以有正負之分。小數部分用二進制(底數 2)小數來表示,這意味著最高位對應著值 ?(2-1),第二位對應著 ?(2-2),依此類推。對于雙精度浮點數,用 11 位表示指數,52 位表示尾數。IEEE 浮點值的格式如圖 1 所示。
     1. IEEE 754 浮點數的格式
    因為用科學記數法可以有多種方式來表示給定數字,所以要規范化浮點數,以便用底數為 2 并且小數點左邊為 1 的小數來表示,按照需要調節指數就可以得到所需的數字。所以,例如,數 1.25 可以表示為尾數為 1.01,指數為 0:
    除了編碼所答應的值的標準范圍(對于 float,從 1.4e-45 到 3.4028235e+38),還有一些表示無窮大、負無窮大、-0 和 NaN(它代表“不是一個數字”)的非凡值。這些值的存在是為了在出現錯誤條件(譬如算術溢出,給負數開平方根,除以 0 等)下,可以用浮點值集合中的數字來表示所產生的結果?! ∵@些非凡的數字有一些不平常的特征。例如,0 和 -0 是不同值,但在比較它們是否相等時,被認為是相等的。用一個非零數去除以無窮大的數,結果等于 0。非凡數字 NaN 是無序的;使用 ==、< 和 > 運算符將 NaN 與其它浮點值比較時,結果為 false。假如 f 為 NaN,則即使 (f == f) 也會得到 false。假如想將浮點值與 NaN 進行比較,則使用 Float.isNaN() 方法。表 1 顯示了無窮大和 NaN 的一些屬性。
    表 1. 非凡浮點值的屬性
    表達式       結果
    Math.sqrt(-1.0) -> NaN
    .0 / 0.0     -> NaN
    1.0 / 0.0    -> 無窮大
    -1.0 / 0.0    -> 負無窮大
    NaN + 1.0    -> NaN
    無窮大 + 1.0   -> 無窮大
    無窮大 + 無窮大  -> 無窮大
    NaN > 1.0     -> false
    NaN == 1.0    -> false
    NaN < 1.0     -> false
    NaN == NaN    -> false
    0.0 == -0.01   -> true
    基本浮點類型和包裝類浮點有不同的比較行為
  使事情更糟的是,在基本 float 類型和包裝類 Float 之間,用于比較 NaN 和 -0 的規則是不同的。對于 float 值,比較兩個 NaN 值是否相等將會得到 false,而使用 Float.equals() 來比較兩個 NaN Float 對象會得到 true。造成這種現象的原因是,假如不這樣的話,就不可能將 NaN Float 對象用作 HashMap 中的鍵。類似的,雖然 0 和 -0 在表示為浮點值時,被認為是相等的,但使用 Float.compareTo() 來比較作為 Float 對象的 0 和 -0 時,會顯示 -0 小于 0。
    浮點中的危險
    由于無窮大、NaN 和 0 的非凡行為,當應用浮點數時,可能看似無害的轉換和優化實際上是不正確的。例如,雖然好象 0.0-f 很明顯等于 -f,但當 f 為 0 時,這是不正確的。還有其它類似的 gotcha,表 2 顯示了其中一些 gotcha。
    表 2. 無效的浮點假定
    這個表達式……   不一定等于……     當……
    0.0 - f     -f         f 為 0
    f < g      ! (f >= g)     f 或 g 為 NaN
    f == f     true         f 為 NaN
    f + g - g     f         g 為無窮大或 NaN
    舍入誤差
    浮點運算很少是精確的。雖然一些數字(譬如 0.5)可以精確地表示為二進制(底數 2)小數(因為 0.5 等于 2-1),但其它一些數字(譬如 0.1)就不能精確的表示。因此,浮點運算可能導致舍入誤差,產生的結果接近 — 但不等于 — 您可能希望的結果。例如,下面這個簡單的計算將得到 2.600000000000001,而不是 2.6:
  double s=0;
  for (int i=0; i<26; i++)
  s += 0.1;
  System.out.PRintln(s);
    類似的,.1*26 相乘所產生的結果不等于 .1 自身加 26 次所得到的結果。當將浮點數強制轉換成整數時,產生的舍入誤差甚至更嚴重,因為強制轉換成整數類型會舍棄非整數部分,甚至對于那些“看上去似乎”應該得到整數值的計算,也存在此類問題。例如,下面這些語句:
  double d = 29.0 * 0.01;
  System.out.println(d);
  System.out.println((int) (d * 100));
    將得到以下輸出:
  0.29
  28
    這可能不是您起初所期望的。
    浮點數比較指南
    由于存在 NaN 的不平常比較行為和在幾乎所有浮點計算中都不可避免地會出現舍入誤差,解釋浮點值的比較運算符的結果比較麻煩。
    最好完全避免使用浮點數比較。當然,這并不總是可能的,但您應該意識到要限制浮點數比較。假如必須比較浮點數來看它們是否相等,則應該將它們差的絕對值同一些預先選定的小正數進行比較,這樣您所做的就是測試它們是否“足夠接近”。(假如不知道基本的計算范圍,可以使用測試“abs(a/b - 1) < epsilon”,這種方法比簡單地比較兩者之差要更準確)。甚至測試看一個值是比零大還是比零小也存在危險 —“以為”會生成比零略大值的計算事實上可能由于積累的舍入誤差會生成略微比零小的數字。
    NaN 的無序性質使得在比較浮點數時更輕易發生錯誤。當比較浮點數時,圍繞無窮大和 NaN 問題,一種避免 gotcha 的經驗法則是顯式地測試值的有效性,而不是試圖排除無效值。在清單 1 中,有兩個可能的用于特性的 setter 的實現,該特性只能接受非負數值。第一個實現會接受 NaN,第二個不會。第二種形式比較好,因為它顯式地檢測了您認為有效的值的范圍。
    清單 1. 需要非負浮點值的較好辦法和較差辦法
  // 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));
  }
  
    不要用浮點值表示精確值
  
    一些非整數值(如幾美元和幾美分這樣的小數)需要很精確。浮點數不是精確值,所以使用它們會導致舍入誤差。因此,使用浮點數來試圖表示象貨幣量這樣的精確數量不是一個好的想法。使用浮點數來進行美元和美分計算會得到災難性的后果。浮點數最好用來表示象測量值這類數值,這類值從一開始就不怎么精確。
  
    用于較小數的 BigDecimal
    從 JDK 1.3 起,Java 開發人員就有了另一種數值表示法來表示非整數:BigDecimal。BigDecimal 是標準的類,在編譯器中不需要非凡支持,它可以表示任意精度的小數,并對它們進行計算。在內部,可以用任意精度任何范圍的值和一個換算因子來表示 BigDecimal,換算因子表示左移小數點多少位,從而得到所期望范圍內的值。因此,用 BigDecimal 表示的數的形式為 :
               -scale
      unscaledValue*10  。
    用于加、減、乘和除的方法給 BigDecimal 值提供了算術運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產生新的 BigDecimal 對象。因此,因為創建對象的開銷,BigDecimal 不適合于大量的數學計算,但設計它的目的是用來精確地表示小數。假如您正在尋找一種能精確表示如貨幣量這樣的數值,則 BigDecimal 可以很好地勝任該任務。
    所有的 equals 方法都不能真正測試相等
    如浮點類型一樣,BigDecimal 也有一些令人希奇的行為。尤其在使用 equals() 方法來檢測數值之間是否相等時要小心。equals() 方法認為,兩個表示同一個數但換算值不同(例如,100.00 和 100.000)的 BigDecimal 值是不相等的。然而,compareTo() 方法會認為這兩個數是相等的,所以在從數值上比較兩個 BigDecimal 值時,應該使用 compareTo() 而不是 equals()。
    另外還有一些情形,任意精度的小數運

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 普陀区| 永兴县| 普洱| 惠来县| 卢氏县| 襄城县| 宜都市| 来安县| 延长县| 湖口县| 淅川县| 延庆县| 延安市| 噶尔县| 怀化市| 广州市| 荆州市| 苍山县| 江西省| 仙桃市| 四川省| 扶绥县| 思南县| 彝良县| 新宁县| 舞阳县| 玛曲县| 绥芬河市| 博客| 海阳市| 宕昌县| 股票| 馆陶县| 正蓝旗| 大兴区| 襄垣县| 丰顺县| 福海县| 垦利县| 永城市| 丁青县|