忍者ブログ
RoboCup Junior Japan Rescue Kanto OB

             2005~2013
[1091]  [1090]  [1089]  [1088]  [1087]  [1086]  [1084]  [1083]  [1082]  [1081]  [1080
死んでません。死んでませんよ。

9月、10月は(今も)色々忙しかったので一切更新していませんでした。が、ブログは生きています。


本日のネタは先日、某SNSから流れてきた話。




このクイズ、一番選ばれているのは10ですね。

数学的に考えても、一般的には答えは10にしかならないのですが・・・。



この場合、この通り答えは11となります。




因みにこれ、CやJavaで似たようなものを書くと10になります。
#include <stdio.h>

int main()
{
    float f=0.0;
    int i=0;
    while(1){
        i+=1;
        f+=0.1;
        if(f>=1)
            break;
    }
    printf("%d\n",i);
    return 0;
}

sh-4.3$ main 
10  




しかし、RubyやPythonで書くと11になります。
f=0.0
i=0
while(1):
    i=i+1
    f=f+0.1
    if f>=1:
        break
    
print i
    

sh-4.3$ python main.py                                                      
11 




今日の記事はここで何が起きているのか・・・という話です。


答えは単純な話、丸め誤差です。

プログラムやっている方なら知識としては知っているはず。でも実感した場面に会うことは少ないですよね。

小数を2進数で記述しようとすると、どこかで桁を丸めないと無限に続いてしまうっていうアレです。


勿論丸めた結果厳密には異なる数字になる訳ですが、これは非常に小さな値です。

普通に表示しただけでは気づかない数値の差となります。
#include <stdio.h>

int main()
{
    float f=0.0;
    int i=0;
    while(i<10){
        i+=1;
        f+=0.1;
        printf ("i=%2d : f=%f\n",i,f);
    }
    return 0;
}

sh-4.3$ main                                                                                                               
i= 1 : f=0.100000                                                                                                          
i= 2 : f=0.200000                                                                                                          
i= 3 : f=0.300000                                                                                                          
i= 4 : f=0.400000                                                                                                          
i= 5 : f=0.500000                                                                                                          
i= 6 : f=0.600000                                                                                                          
i= 7 : f=0.700000                                                                                                          
i= 8 : f=0.800000                                                                                                          
i= 9 : f=0.900000                                                                                                          
i=10 : f=1.000000 









ところが、これを表示桁30桁にすると・・・
#include <stdio.h>

int main()
{
    float f=0.0;
    int i=0;
    while(i<10){
        i+=1;
        f+=0.1;
        printf ("i=%2d : f=%.30f\n",i,f);
    }
    return 0;
}

sh-4.3$ main                                                                                                               
i= 1 : f=0.100000001490116119384765625000                                                                                  
i= 2 : f=0.200000002980232238769531250000                                                                                  
i= 3 : f=0.300000011920928955078125000000                                                                                  
i= 4 : f=0.400000005960464477539062500000                                                                                  
i= 5 : f=0.500000000000000000000000000000                                                                                  
i= 6 : f=0.600000023841857910156250000000                                                                                  
i= 7 : f=0.700000047683715820312500000000                                                                                  
i= 8 : f=0.800000071525573730468750000000                                                                                  
i= 9 : f=0.900000095367431640625000000000                                                                                  
i=10 : f=1.000000119209289550781250000000 

こんな感じで理論上入るべき値と違うものが入っていることが確認できます。これが誤差です。










これはRubyやPythonのように結果が11になる言語でも同様です。
f=0.0
i=0;
while(i<10):
    i=i+1
    f=f+0.01
    print "i={0:2d}".format(i)+": f="+str(f)
    print "i={0:2d}".format(i)+": f={0:.30f}".format(f)



sh-4.3$ python main.py                                                                                                     
i= 1: f=0.01                                                                                                               
i= 1: f=0.010000000000000000208166817117                                                                                   
i= 2: f=0.02                                                                                                               
i= 2: f=0.020000000000000000416333634234                                                                                   
i= 3: f=0.03                                                                                                               
i= 3: f=0.029999999999999998889776975375                                                                                   
i= 4: f=0.04                                                                                                               
i= 4: f=0.040000000000000000832667268469                                                                                   
i= 5: f=0.05                                                                                                               
i= 5: f=0.050000000000000002775557561563                                                                                   
i= 6: f=0.06                                                                                                               
i= 6: f=0.060000000000000004718447854657                                                                                   
i= 7: f=0.07                                                                                                               
i= 7: f=0.070000000000000006661338147751                                                                                   
i= 8: f=0.08                                                                                                               
i= 8: f=0.080000000000000001665334536938 
i= 9: f=0.09                                                                                                               
i= 9: f=0.089999999999999996669330926125                                                                                   
i=10: f=0.1                                                                                                                
i=10: f=0.099999999999999991673327315311  


このようにデフォルトの変数出力では気が付かないレベルの桁数が丸められているわけです。


で、比べて見ればわかると思うのですが、CとPythonで30桁の方の値が違います。

これはfloatの規格が違うからで、PythonやRubyなど、比較的新しいスクリプト言語では

大体全部IEEE 754系を採用しています。


一方C言語は、IEEE 754が制定される前の言語な上に、

そもそもC言語はコンパイラ言語なのでこの辺りはコンパイラの規格に依存します。

つまりソースを見ただけでは挙動が判断できません。JavaはIEEE Standard No.754-1985


因みに手元の環境ですと、C、C+、C#、Javaは全て10が出力されて、スクリプト系は全部11でした。

あ、Haskellは試してないです。誰か頑張れ


という訳で、理論上の数値が1.0になった際に、

値が理論値より大きい方に丸まってる環境では10が出力されて、

小さい方に丸まっている言語では11が出力されていた訳です。



勿論どちらに丸まるかは数によっても変わってしまいますので、

小数を使うときは気を付けないといけないということなんですね。
つまり10になるか11になるかはIEEE754のfloatの1.0の時の挙動を知っていないとわからないのです。

というのが本日の丸め誤差です。

桁落ち程ではないですが、小数を使った数値計算系のプログラムを普段書かない人は

意外と忘れがちなので要注意なのです。桁落ちは意識してても無理


以上。当ブログは生きています。



(^・ω・)ノ curonet at RadiumProduction

11/13追記:Google先生が少数という謎変換を生み出していることに気が付きました(てへぺろ
Comments
※コメントは内容確認後に手動で公開するようにしております。反映までしばらくお待ちください。
Your Name
Title
color
Comment
 

カレンダー
02 2024/03 04
S M T W T F S
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
最新CM
[05/09 ONE RoboCuper]
[05/07 HDD ほしいよー]
[04/21 ブラック3辛]
[12/26 bols-blue]
[06/08 ONE RoboCuper]
かうんた
カウンター カウンター
らじぷろ目次
らじぷろ検索機
プロフィール
HN:
Luz
性別:
男性

PR

忍者ブログ 2007-2021,Powered by Radium-Luz-Lα+-Rescatar in RadiumProduction [PR]


Related Posts Plugin for WordPress, Blogger...