
マイケル
みなさんこんにちは!
マイケルです!
マイケルです!

エレキベア
クマ〜〜〜〜

マイケル
今日はプログラミングに関するお話です!
いきなりですが、下記コードはどのような結果になると思いますか?
いきなりですが、下記コードはどのような結果になると思いますか?
↑1.0になるまで加算するプログラム

エレキベア
1になるまで加算したら処理が終了するクマ?

マイケル
それでは結果をみてみましょう・・・
↑終わらない

マイケル
処理が終了せずにひたすら加算し続けます・・・。

エレキベア
恐ろしいクマ・・・。
浮動小数の誤差

マイケル
これは ==を使って同値比較を行なっている ため起こる問題です。
floatのような浮動小数では若干の誤差が出てしまう ため、同一ではない判定になってしまうんですね・・・。
floatのような浮動小数では若干の誤差が出てしまう ため、同一ではない判定になってしまうんですね・・・。

エレキベア
count == 1.0f の部分クマね・・・

マイケル
なんで誤差が発生してしまうのか、少し考えてみよう!
浮動小数とは

マイケル
まず、そもそも浮動小数とは何かについて!
これは コンピュータで小数同士の計算を楽に行うための表現方法 です。
これは コンピュータで小数同士の計算を楽に行うための表現方法 です。

マイケル
おおざっぱにいうと、
符号部 → 正の値(0)、負の値(1)
指数部 → 計算のベース値 (2のべき乗)
仮数部 → ベース値から近づける値 (ベース値を2のべき乗で除算)
となります!
符号部 → 正の値(0)、負の値(1)
指数部 → 計算のベース値 (2のべき乗)
仮数部 → ベース値から近づける値 (ベース値を2のべき乗で除算)
となります!

エレキベア
(全然わからんクマ・・・)

マイケル
float型のような単精度浮動小数(32ビット)と、
double型のような倍精度浮動小数(64ビット)では、それぞれ下記のような構成になっています。
double型のような倍精度浮動小数(64ビット)では、それぞれ下記のような構成になっています。
↑半精度浮動小数の構成(float型)
↑倍精度浮動小数の構成(double型)

マイケル
具体例として、13.75を単精度浮動小数で表したものを下記の図に表します!
↑13.75を浮動小数で表現したもの

マイケル
指数部でベースとなる8を設定して、
仮数部で5.75を加算することで表現しています。
仮数部で5.75を加算することで表現しています。

マイケル
一点気をつけないといけないのは、指数部は小数も表現するため、通常 0〜255 の範囲が -127〜128の範囲になっていること!
計算する時には127を引いてべき乗する必要があります!
計算する時には127を引いてべき乗する必要があります!

エレキベア
なんとなく分かったような分からないようなクマ・・・

マイケル
とりあえずコンピュータは2進数(2のべき乗)で表現している
ということだけ抑えておこう!
ということだけ抑えておこう!
なぜ誤差が起きるのか

マイケル
それではなぜ浮動小数では何故誤差が起きてしまうのかについて、考えてみよう!
調べたところ、こちらの記事で分かりやすく説明してくれていました!
調べたところ、こちらの記事で分かりやすく説明してくれていました!

マイケル
小数は 0になるまで2で乗算し続ける ことで2進数に変換するけど、
ほとんどの小数は綺麗に0にならないから誤差が出てしまうということですね・・・。
ほとんどの小数は綺麗に0にならないから誤差が出てしまうということですね・・・。

エレキベア
0.1〜0.9の中で綺麗に表せるのは0.5だけ
というのは驚きクマ・・・
というのは驚きクマ・・・

マイケル
試しに0.1がどんな値になってるのか見てみましょう!
適当に70桁ほど指定して表示してみます。
適当に70桁ほど指定して表示してみます。

マイケル
float型、double型でそれぞれこれだけの誤差が出ているのが分かります・・・

エレキベア
恐ろしいクマ・・・

マイケル
浮動小数で表現するとどうなるかも見てみましょう!
こちらは下記サイトのツールを使用させていただきました!
こちらは下記サイトのツールを使用させていただきました!
↑0.1を浮動小数に変換した結果

マイケル
どちらも2^-4=0.0625 をベースとして、
なんとか近づけようとしていることが分かりますね。
なんとか近づけようとしていることが分かりますね。

エレキベア
誤差が出る理由はなんとなく分かったクマ
計算機イプシロンを用いた比較

マイケル
この誤差をどのように比較するのがいいかについてですが、
会社で 計算機イプシロンという値を使って比較するのがよいという話を伺いました!
会社で 計算機イプシロンという値を使って比較するのがよいという話を伺いました!

マイケル
「1より大きい最小の数と1との差」のことらしく、
このイプシロンの範囲を許容範囲とする考え方です!
このイプシロンの範囲を許容範囲とする考え方です!

エレキベア
そんな定義があったクマね
C#で用意されているイプシロン

マイケル
調べたらC#には Single.Epsilon、Double.Epsilon
が用意されている!
みたいだったので使ってみたのですが・・・
が用意されている!
みたいだったので使ってみたのですが・・・
↑C#のイプシロンを使った比較

マイケル
なかなか思うような比較結果にならず・・・

エレキベア
なんでクマ・・・

マイケル
どうやらC#に用意されているイプシロンは
計算機イプシロンではなく、「浮動小数点演算の丸の相対誤差の上限(丸め誤差発生時にズレるであろう最大値)」らしく・・・。
計算機イプシロンではなく、「浮動小数点演算の丸の相対誤差の上限(丸め誤差発生時にズレるであろう最大値)」らしく・・・。

マイケル
どうやら自分で求めるしかなさそうです・・・。

エレキベア
ややこしすぎるクマ・・・。

マイケル
ちなみにUnityで用意されている Math.Epsilonもこちらと同じ値のようでした・・・。
計算機イプシロンを求める

マイケル
というわけで自分で求めてみましょう!
調べたところ 変数をひたすら2で割り続け、ゼロになった瞬間のひとつ前を捉えるというアルゴリズムが有名なようです!
調べたところ 変数をひたすら2で割り続け、ゼロになった瞬間のひとつ前を捉えるというアルゴリズムが有名なようです!
↑イプシロンを求めるアルゴリズム
[出力結果]
1.1920929E-07
2.220446049250313E-16

マイケル
出力結果を見た感じ、うまく求められていそうです!

エレキベア
やったクマ〜〜〜
計算機イプシロンを使って比較

マイケル
それではこちらを使って比較してみましょう!
↑イプシロンを用いた比較

マイケル
うまく比較できました!!

エレキベア
やったクマ〜〜〜〜〜!!!!
だめだった・・・

マイケル
これで解決!!
・・・と思いましたが、だめなパターンがありました。
・・・と思いましたが、だめなパターンがありました。

マイケル
下記のように、10まで加算したり、0.01刻みで加算したりなど、
加算する回数が多くなれば誤差も大きくなり、イプシロンの範囲に収まらなくなるみたいです・・・。
加算する回数が多くなれば誤差も大きくなり、イプシロンの範囲に収まらなくなるみたいです・・・。
↑加算する回数が多くなるとNG

エレキベア
どうすればいいクマ・・・
どうするのが正しい?

マイケル
調べてみると、下記記事でも似たようなことが書かれていて、
複数回演算することによって誤差が積み重なることがあるため、
実際のプロジェクトでは本来の数値よりもすこし大きめの数値で定義して運用する とのことでした…。
複数回演算することによって誤差が積み重なることがあるため、
実際のプロジェクトでは本来の数値よりもすこし大きめの数値で定義して運用する とのことでした…。

マイケル
つまり、少し大きめの許容範囲の誤差を自分で定義するしかなさそうですね・・・。
↑許容範囲のイプシロンを大きめに設定する

マイケル
これでうまく比較はできました!
許容範囲はそのケースによって調整するのがよさそうです・・・。
許容範囲はそのケースによって調整するのがよさそうです・・・。

エレキベア
なんかあいまいな感じもするクマね・・・。

マイケル
その他の方法だと、丸めて比較したり、10、100倍して型変換して比較
する方法もありそうですね・・・。
する方法もありそうですね・・・。

エレキベア
比較一つも大変クマね・・・。
おわりに

マイケル
というわけで今回は浮動小数の同値比較についてでした!
どうだったかな?
どうだったかな?

エレキベア
結局パッとしない答えになってしまったクマね・・・

マイケル
正直もっといい方法もありそうだよね・・・
(何かいい方法があれば、ぜひコメント蘭に指摘お願いします!)
(何かいい方法があれば、ぜひコメント蘭に指摘お願いします!)

マイケル
とりあえず忘れちゃいけないのは
浮動小数を扱う時には誤差に注意すること!!
これは頭の片隅にいれておきましょう!
浮動小数を扱う時には誤差に注意すること!!
これは頭の片隅にいれておきましょう!

エレキベア
確かによく使うクマだし、
一歩間違えればバグに繋がるクマからね
一歩間違えればバグに繋がるクマからね

マイケル
数値同士の比較がうまくいかないときには今回の話を思い出しましょう。

マイケル
それでは今日はこの辺で!
アデュー!!!
アデュー!!!

エレキベア
クマ〜〜〜〜〜
【C#】浮動小数の誤差とイプシロンを用いた同値比較について【Epsilon】 〜完〜