JWorld@TW the best professional Java site in Taiwan
      註冊 | 登入 | 全文檢索 | 排行榜  

» JWorld@TW » Java 新手區  

按列印兼容模式列印這個話題 列印話題    把這個話題寄給朋友 寄給朋友    訂閱主題
reply to topicthreaded modego to previous topicgo to next topic
本主題所含的標籤
作者 [FAQ] 為何 1.0 - 0.8 不是 0.2? [精華]
worookie

Small Ship

版主

發文: 2092
積分: 21
於 2003-10-14 01:40 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
(這是在 BBS 上常常會出現的問題, 故收錄至新手區的 FAQ.)
除了解釋為什麼之外, 亦歡迎您提出解決的方法.


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
ymshin





發文: 277
積分: 4
於 2003-10-14 01:56 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
灌水文章

anthonycs edited on 2004-04-10 23:24
reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:ymshin]
qop

Newbie



發文: 204
積分: 3
於 2003-10-14 02:34 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
http://www.way-to-win.com/~web/ace/90tku01.pdf

http://people.uncw.edu/tompkinsj/121/Numbers/Reals.htm

解決的方式可以使用 long 或.....自訂數值類別以整數去模擬
看精確度要到第幾位囉~

等別人實作中....Big Smile


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:qop]
ymshin





發文: 277
積分: 4
於 2003-10-14 02:40 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
qop wrote:
http://www.way-to-win.com/~web/ace/90tku01.pdf

http://people.uncw.edu/tompkinsj/121/Numbers/Reals.htm

解決的方式可以使用 long 或.....自訂數值類別以整數去模擬
看精確度要到第幾位囉~

等別人實作中....Big Smile


嗯, 這個解法不正確說~ 論壇以前有人問過類似題目, 企找吧~ Big Smile


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:ymshin]
qop

Newbie



發文: 204
積分: 3
於 2003-10-14 10:01 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
ymshin wrote:
嗯, 這個解法不正確說~ 論壇以前有人問過類似題目, 企找吧~ Big Smile

還有正確答案喔 ??
不是只要達到目的都算對嗎 ?
現在流行建構式數學哩 :p


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
item1394

松隆子



發文: 81
積分: 4
於 2003-10-14 14:45 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
簡單的說,問題出在"IEEE 754 floating-point arithmetic" ,在Java語言裡遵守這項規則,這項規則不使用小數點來表示數字 ! 也就是說沒有小數點這種東西,相反的它使用分數及指數來表示小數
例如:
0.5 = 1/2
0.75 = 1/2 + 1/4
0.875 = 1/2 + 1/4 + 1/8
0.1 = 1/16 + 1/32 + 1/256 + 1/512 +1/4096 + 1/8192 + ....

知道了嗎?到最後,這一連串的分數將會無止境的下去,也因此造成了浮點數運算上的誤差

ps. 有興趣深入了解的人可到下面網址看看
http://java.sun.com/developer/JDCTechTips/2003/tt0204.html#2
前面只是我簡單的介紹,之後我會試試將全文翻得完整一點,盡力而為囉
還請各位前輩多多指教!!
Sleepy


item1394 edited on 2004-06-05 22:10
reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:item1394]
metavige

metavige

版主

發文: 2133
積分: 10
於 2003-10-14 17:03 user profilesend a private message to usersend email to metavigereply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
item1394 wrote:
[引言過長]


很棒呢
我還在想為甚麼 1.0 - 0.8 <> 0.2 呢~

原來是因為電腦沒辦法運算小數點以下的部分呢~
能找到這篇文章, 很厲害喔~ Smile


anthonycs edited on 2004-04-10 23:25
reply to postreply to post
請各位新手參考 論壇規範Java 新手 FAQ
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
item1394

松隆子



發文: 81
積分: 4
於 2003-10-14 17:40 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
呵呵!!
這都是全文檢索的功勞喔!!
因為有些地方翻的不甚順暢
請各位前輩對我翻譯的文章多多指教!!
ps.翻得好累啊~~


item1394 edited on 2004-06-05 21:22
reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
item1394

松隆子



發文: 81
積分: 4
於 2003-10-14 18:02 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
假設你現在正在設計某些 Java 程式,其中牽涉到浮點數的計算,假定你的程式碼中設有變數其值等於0.1,然後連加它自己兩次,範例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Fp1
{
     public static void main(String argv[])
     {
          double val1 = 0.1;
          double val2 = 0.3;
 
          double d = val1 + val1 + val1;
 
          if (d != val2)
          {
               System.ou.println(" d != val2 ");
          }      
          
          System.out.println
          ( "d - val2 =" + (d - val2) );
     }
}

每個人都知道
0.1 + 0.1 + 0.1 = 0.3
至少在數學上是這樣的, 但是當你執行 Fp1 這個程式時,你會發現結果不再是如此,程式執行結果如下:
1
2
         d != val2
         d - val2 = 5.551115123125783E-17

這個結果似乎是說 0.1 + 0.1 + 0.1 其結果跟 0.3 差了大約 5.55e-17

問題出在哪呢?這個問題就是 IEEE754 浮點數運算( IEEE 754 floating-point arithmetic)
,這可在 Java 語言中找到, Java 語言並不使用小數點(或十進位)去表現數字,相反的它採用(二進位)分數和指數,要實際了解以上所說是什麼意思,我們考慮試著寫出數個小數以分數表示的式子
0.5 = 1/2
0.75 = 1/2 + 1/4
0.875 = 1/2 + 1/4 + 1/8
0.1 = 1/16 + 1/32 + 1/256 + 1/512 +1/4096 + 1/8192 + ...
最後這一個級數是無窮止境的繼續下去,這意味著 0.1 不能準確的以浮點數型態表現,這無窮級數必須在某一點切斷, 做一些捨去或進位的動作...等等, 目的要以64位元double型態表現,而這將會導致一些誤差
底下是另外一個例子,以浮點數運算的結果將會與你預期以數學規則作運算的結果有所不同
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Fp2
{
     public static void main(String argv[])
     {
       double val1 = 12345.0;
       double val2 = 1e-16;
         
       if(val1 + val2 == val1)
       {
            System.out.println(" val1 + val2 == val1 ");
       }
     }
}

其結果如下:
1
val1 + val2 ==val1

就數學觀點來看, 如果
a > 0 && b > 0

a + b !=a
但是程式跑出來的結果明顯違反這個規則, 這問題出在浮點數型態變數的精確度,所謂的精確度就是用多少位元( bits )去表現這一個值(二進位的分數),而且其範圍與可用浮點數型態表示的值之範圍有所不同,對於Java double 形態的變數來說,有53bits的精確度,或是大約16位數(十進位),上述的例子包含一個精確度大約20位數,因為上式可改寫成
1.2345e+4 + 1e-16
因為double形態變數的精確度不足以表示這個加法的結果,所以 1e-16實際上被省略掉了
下面另一個例子是描述數學規則是如何被破壞的
1
2
3
4
5
6
7
8
9
10
11
public class Fp3
{
     public static void main(String argv[])
     {
          double d1 = 1.6e308;
          double d2 = d1 * 2.0 / 2.0;
 
          System.out.println( d1 );
          System.out.println( d2 );
     }
}

如果d1乘以2.0且除以2.0其結果應為d1,是吧?很不幸的結果並不是如此,其結果實際如下:
1
2
1.6e308
                Infinity

首先d1乘以2.0其值已超過精確度所能提供的位數,而之後再除以2.0已於事無補, double 型態變數其最大值大約為1.8e308, 1.6e308乘以2.0, 其結果等於3.2e308, 顯示出來的結果及為正無限大(positive infinity)

再來看看其他例子,如果你曾做過數學整數運算就會知道任何數除以零均是無意義的( ArithmeticException ),但是同樣的運算若以浮點數做處理又會如何呢?例子如下:
1
2
3
4
5
6
7
8
9
public class Fp4
{
     public static void main(String argv[])
     {
         System.out.println( 1.0/0.0 );
         System.out.println( 1.0/-0.0 );
         System.out.println( 0.0/0.0 );
     }
}

其結果為
1
2
3
         Infinity
        -Infinity
         NaN

這個程式對於以浮點數除以零這個式子沒有丟出例外(Exception),當一個正數或負數除以零時,其結果均為無限大

上面的例子表示出事實上不只有一個零,其實有正負零之分,當決定除式結果是正無限大或負無限大時,零之正負將會被考慮進計算之中
那麼第三個式子又如何呢? 0.0/0.0 其結果為 NaN(not a number)
正負零與NaN有一些奇怪的特性,來看看以下的例子來了解這句話
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Fp5
{
     public static void main(String argv[])
     {
          double d1 = -0.0;
          double d2 = +0.0;
          if(d1<d2)
          { System.out.println( -0.0 < +0.0); }
          
          double d3 = 0.0/0.0;
          if(d3 == d3)
          { System.out.println( " d3 == d3 " ); } 
     }
}

如果你執行 Fp5 這個程式,你將會看不到任何東西,如同你所看到之前的例子,零的正負號會影響計算, 就像 1.0/ -0.0 == -infinity 和 1.0/ +0.0 == infinity 但這並不能就因此說 -0.0 < +0.0,上述的例子說明了這件事( d1不小於d2,所以並不顯示任何東西), 不管就你的觀點 -0.0是否跑得比 +0.0還"前面"
Fp5 也說出了一個重點那就是 NaN 是不受控制的( unordered ), 所以d3並不等於它自己,這是一個方法(way)你可以寫出一個方法(method)去分辨一個數是否是NaN(not a number),如果一個數不等於它自己那麼它就是NaN,這種方法(method)事實上已經存在,你可以看看 Float.isNan和Double.isNaN這兩個方法

我們來看看最後這個例子,我們用這個例子來試著描述一個規則關於之前所討論到奇特的浮點數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public calss Fp6
{
      static double[] list = 
       {
            Double.NEGATIVE_INFINITY,
            -0.0,
            +0.0,
            Double.MIN_VALUE,
            Double.MAX_VALUE,
            Double.POSITIVE_INFINITY
        };
    
        public static void main(String argv[]) 
        {
            for (int i = 0; i < list.length; i++) 
            {
                System.out.println(list[i]);
            }
        }
}

其結果為
1
2
3
4
5
6
            -Infinity
            -0.0
             0.0
             4.9E-324
             1.7976931348623157E308
             Infinity

注意 Double.MAX_VALUE 和 Double.POSITIVE_INFINITY 是不同的值
這篇文章提出有關浮點數運算的一些特色.
在使用浮點數時必須要小心,
因為它所產生出來的結果將會與你所預期的不同,
如果你僅僅是應用數學法則去使用它的話

未完...


item1394 edited on 2004-06-07 08:53
reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
T55555

Java, Ruby, Haskell

版主

發文: 1026
積分: 24
於 2003-10-15 00:14 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list

這意味著 0.1 不能準確的以浮點數型態表現


[題外話]:
哈哈, 既然 0.1 不能準確的以浮點數型態表現, 為什麼
System.out.println(0.1) 仍然準確打印 "0.1" 呢 ?
如果您想知道, check the FloatingDecimal class.
FloatingDecimal 做了很多事情...


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:item1394]
worookie

Small Ship

版主

發文: 2092
積分: 21
於 2003-10-15 01:19 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
item1394 表現很好 加油!

reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
worookie

Small Ship

版主

發文: 2092
積分: 21
於 2003-10-15 01:26 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
很多人解釋了為什麼, 不過好像還沒看到怎麼辦的深入文章. 期望有人發表.

reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
koji

秒速5センチメートル

站長

發文: 8415
積分: 19
於 2003-10-15 02:15 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
BigDecimal(java.lang.String)
不要用
BigDecimal(double val)
1
2
3
4
BigDecimal b1 = new BigDecimal("1");
BigDecimal b2 = new BigDecimal("0.8");
b1 = b1.subtract(b2 );
System.out.println(b1.toString);


and in javadoc
Translates a double into a BigDecimal. The scale of the BigDecimal is the smallest value such that (10scale * val) is an integer.
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances notwithstanding.

The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.

這樣不知好不好

koji


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:koji]
jini

SoftLeader Taiwan

版主

發文: 1266
積分: 23
於 2003-10-15 02:19 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
koji wrote:
[引言過長]


唯一解法 當然好呀 ^^~

附註:
通常我們會希望得到的還是 double

加 : return b1.add(b2).doubleValue();
減 : return b1.subtract(b2).doubleValue();
乘 : return b1.multiply(b2).doubleValue();
除 : return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

除法 scale 是小數點後幾位數, ROUND_HALF_UP 就是四捨五入的意思


anthonycs edited on 2004-04-10 23:25
reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
item1394

松隆子



發文: 81
積分: 4
於 2003-12-26 10:01 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
參考網址改為
http://java.sun.com/developer/JDCTechTips/2003/tt0204.html#2


reply to postreply to post
Once an angel,
now an abomination.
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
小a





發文: 68
積分: 0
於 2011-01-18 23:19 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
或許....不知道這辦法行不行的通@@

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  public static double getResult(double a, double b, double c)
  {
    byte count = 0;
 
    for(int i = 0; ; i++)
    {
      if(a < 1.0 || b < 1.0 || c < 1.0)
      {
        a *= 10;
        b *= 10;
        c *= 10;
 
        count++;
      }
      else
      {
        break;
      }
    }
 
    return (double)((a + b + c) / Math.pow(10, (int)count));
  }


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:小a]
小a





發文: 68
積分: 0
於 2011-01-18 23:24 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
如果是依照這帖的題目來看....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  public static double getResult(double a, double b)
  {
    byte count = 0;
 
    for(int i = 0; ; i++)
    {
      if(a < 1.0 || b < 1.0)
      {
        a *= 10;
        b *= 10;
 
        count++;
      }
      else
      {
        break;
      }
    }
 
    return (double)((a - b) / Math.pow(10, (int)count));
  }


1
2
3
System.out.println(getResult(1.25, 0.8) == 0.45); 
 
true


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:小a]
小a





發文: 68
積分: 0
於 2011-01-18 23:27 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
當然.....這並不能解決全部的問題
小數點後面兩位還可以....但是 0.4555555 4.5依然大於1

所以不能只判斷是否大於1.....

但是夜深了~~~先來去睡XD

((這帖那麼久了, 不知道有沒有人看的到@@
((這是剛剛一個朋友看到, 問我, 才想到.......


reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:小a]
Alumisun





發文: 15
積分: 0
於 2013-05-05 23:03 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
理論 item1394 仁兄已經說明很多

其實原理重點很簡單
不管是 float or double 其 判斷式都有精度限制
所以重點歸納以下
不可將浮點變數使用 "==" 或 "!=" 與任何數字比較 (嚴重錯誤)

正確浮點數判斷式 應該要使用 ">=" 跟 " <="


Alumisun edited on 2013-05-05 23:05
reply to postreply to post
作者 Re:[FAQ] 為何 1.0 - 0.8 不是 0.2? [Re:worookie]
coolmaning





發文: 34
積分: 0
於 2013-10-23 11:44 user profilesend a private message to userreply to postreply to postsearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
java的浮點數運算所得出的值只是近似值

根據「Think in Java」這本書
若考慮精確度的問題可以用「BigInteger」或「BigDecimal」這兩個函式去運算
BigInteger:可以很精確地表示任意長度的整數數值,不會在運算過程中喪失任何資訊。
BigDecimal:提供任意精度的定點數,用於浮點數。

還請各位高手指教。


reply to postreply to post

if(atHome == true)
eat();
playGame();
sleep();
else
goHomeNow();
» JWorld@TW »  Java 新手區

reply to topicthreaded modego to previous topicgo to next topic
  已讀文章
  新的文章
  被刪除的文章
Jump to the top of page

JWorld@TW 本站商標資訊

Powered by Powerful JuteForum® Version Jute 1.5.8