スポンサーリンク
※サイト運営にサーバーは必須です※
~ ロリポップ! はコスパのよい初心者向けサーバーです~
目次
はじめに
アンダーフローという言葉を調べたが、どこまでの範囲をアンダーフローというかよくわからなくなってきた。
自分がわかったこと、逆にわからなかったことをまとめておく。
アンダーフローとは
・演算の結果の絶対値が小さくなった時に、正確に表現できない状態
・「下位桁溢れ」とも言う
以下の場合、アンダーフローというのだろうか?
1番目のケース:0に近いが0でない小さな数字が、0として表示される
2番目のケース:0に近いが0でない小さな数字が、不正確に表示される
3番目のケース:大きな数字に対して。小さな数字を足しても反映されない
1番目のケース:0に近いが0でない小さな数字が、0として表示される
float型であれば、1.401298E-45 (float型で表現できる最も0に近い小さな数字)より小さい数字を表現しようとした時に、0と表示される「場合がある」。
例えば、10の-46乗をfloat型で表現しようと試みた時に、0と表示される。
※ここで、「場合がある」というぼやけた表現を使ったのは10の-45乗という1.401298E-45より小さな数字を取り扱っても、0にならなかったため。プログラムの動きを見ると、1.401298E-45の半分以下になった時に、0と表示されている。
2番目のケース:0に近いが0でない小さな数字が、不正確に表示される
float型は2^(-127乗)より小さい数になると、精度が悪くなる。(10進法に直すと10の-38乗オーダーより小さい領域)。
※どうして精度が悪くなるかという理由については、以下の記事を参照してください。この後の議論では、この記事で書かれている程度のことはわかっている前提で進める。
例えば、10の-45乗を表示しようとしても、結果は1.401298E-45となる。0.4 E-45分ずれている。これは不正確である。
この場合、float型は10の-45乗を表現できていると言っていいのだろうか?
そして、このケースをアンダーフローと言っていいのだろうか?
この部分は、人によって意見が分かれそうな部分である。
仮にこの場合をアンダーフローと言っていいのなら、以下のような疑問がわく。
疑問1:2^(-127乗)より小さい数をアンダーフローと言っていいのか?
例えば、以下のサイトは、非正規化数(float型であれば2^(-127乗)より小さい領域)でしか表現できない状況は、アンダーフローに含まれるという定義をしている
疑問2:非正規化数で、ピッタリ表現できる数字を宣言した時に、アンダーフローと言っていいのか?
例えば、1.401298E-45という数字を宣言した時に、この数字は2^(-127乗)より小さい数だが、ピッタリ表現できている。この時、アンダーフローが起きているといっていいのか?
もしも、この場合に対しては、アンダーフローが起きていないと主張するなら、許される精度はどこまでか?
1.401298E-45を表示しようとして、1.401298E-45と表示されるのはアンダーフローか?
1.40E-45を表示しようとして、1.401298E-45と表示されるのはアンダーフローか?
1.39E-45を表示しようとして、1.401298E-45と表示されるのはアンダーフローか?
1.30E-45を表示しようとして、1.401298E-45と表示されるのはアンダーフローか?
1.0E-45を表示しようとして、1.401298E-45と表示されるのはアンダーフローか?
3番目のケース:大きな数字に対して。小さな数字を足しても反映されない
例えば、float型であれば10の7乗(10000000)に対して、1を足しても10000001とはならず、10000000である。
アンダーフローが、扱う数値が細かくて、正しく扱えていないこと意味するのであれば、この状況はアンダーフローというのか?
結局、どこまでがアンダーフロー?
1番目のケース:0に近いが0でない小さな数字が、0として表示される
2番目のケース:0に近いが0でない小さな数字が、不正確に表示される
3番目のケース:大きな数字に対して。小さな数字を足しても反映されない
1番目のケースはアンダーフローと言っても問題ない。アンダーフローの記事をいくつか見たが、このケースこそがアンダーフローであると主張する記事もあった。
2番目のケースと3番目のケースはよくわからない。人によって言っていることがぶれている。私も何を信じればいいのかわからない。
ソースコード(言語はC#)
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace UnderFlow01 { class Program { static void Main(string[] args) { float f; f = (float)System.Math.Pow(10, -45); Console.WriteLine(f); f = 0.51f * (float)System.Math.Pow(10, -45); Console.WriteLine(f); f = 0.50f * (float)System.Math.Pow(10, -45); Console.WriteLine(f); f = (float)System.Math.Pow(10, -46); Console.WriteLine(f); f = 1000000f; f += 1; Console.WriteLine(f); f = 10000000f; f += 1; Console.WriteLine(f); f = 1f; f += 0.000001f; Console.WriteLine(f); f = 1f; f += 0.0000001f; Console.WriteLine(f); //doubleの場合 double d; d=System.Math.Pow(10,14); d += 1; Console.WriteLine(d); d = System.Math.Pow(10, 15); d += 1; Console.WriteLine(d); } } } |
実行結果
1.401298E-45
1.401298E-45
0
0
1000001
1E+07
1.000001
1
100000000000001
1E+15
※以上のソースコードをコンパイルする時に、「演算のオーバーフローおよびアンダーフローのチェック」の項目にチェックを入れてみたが、何も警告されなかった。
「演算のオーバーフローおよびアンダーフローのチェック」の項目にチェックを入れる方法は以下の記事を参照してください。
~ギャンブルに絶対儲かる必勝法があるのだろうか?~
私(サイト主)はこの疑問に対して非常に興味を持ち、プログラミングで検証してみました。
このサイトを応援してもいいかなと思う人はぜひとも購入を検討してみてください。