RoboCup Junior Japan Rescue Kanto OB
2005~2013
2005~2013
死んでません。死んでませんよ。
9月、10月は(今も)色々忙しかったので一切更新していませんでした。が、ブログは生きています。
本日のネタは先日、某SNSから流れてきた話。
9月、10月は(今も)色々忙しかったので一切更新していませんでした。が、ブログは生きています。
本日のネタは先日、某SNSから流れてきた話。
#きょうのつぶやきクイズ #codeiq
— CodeIQ [コードアイキュー] (@codeiq) 2016年11月7日
以下の #ruby ソースの出力結果は?正解は18時!
i = 0
cnt = 0
while true
i += 0.1
cnt += 1
break if i >= 1
end
puts cnt
このクイズ、一番選ばれているのは10ですね。
数学的に考えても、一般的には答えは10にしかならないのですが・・・。
#きょうのつぶやきクイズ #codeiq #ruby
— CodeIQ [コードアイキュー] (@codeiq) 2016年11月8日
正解発表のお時間です!
本日の正解は「11」でした!
25%の方が正解でした!おめでとうございます♪
【プログラミングクイズは #codeiq で!⇒ https://t.co/eajD8gDb9c 】 pic.twitter.com/G7HFvE0JMy
この場合、この通り答えは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先生が少数という謎変換を生み出していることに気が付きました(てへぺろ
カレンダー
最新CM
カテゴリー
らじぷろ目次
らじぷろ検索機
最新記事
(01/01)
(08/27)
(04/29)
(01/01)
(11/20)
(09/06)
(09/04)
(08/09)
(08/06)
(07/27)
(05/29)
(03/15)
(01/01)
(05/07)
(01/11)
プロフィール
HN:
Luz
性別:
男性
アーカイブ