torus711 のアレ

主に競技プログラミングの問題について書きます.PC 以外だと数式が表示されないかもしれないです

浮動小数点数の誤差に怯えながらも比較をしたい場合がある話

はじめに

 最近出題された某問題の影響で,浮動小数点数の誤差について注目が集まっています.さて,あの問題では入力を受け取っただけ・乗算しただけで誤差が出るという話でしたが,幾何問題など,更に比較などの演算も行いたい場合があります.しかしながらもう知られているように,何も考えずに書くと誤差で壊れます.そこで今回は,誤差を考慮しながら等値比較・大小比較を行う際のイディオムについて書いてみます.
 なお,そもそも論としては整数演算だけでやれるならその方が安全です.後述の $\epsilon$ の値を変えるだけで通ったり通らなかったりすることがあるので…….

準備

 誤差を考慮しての比較ではよく EPS という名前の極小の定数($10^{-8}$ とか $10^{ -12 }$ とか)を定義しておいてこれを足したり引いたりすることで「許容誤差」のようなものを実現します.EPS は epsilon から来ているようなので,本文中では $\epsilon$ と書きます.

等値比較

 2 つの実数 $a, b$ が等しいかどうかを判定したいとします.これを $a = b$ とそのまま書いてしまうと数学的には等しいはずの値であっても蓄積した誤差によって等号が成り立たないかもしれません.等値比較では,「等しい」と言う代わりに「差が十分小さい」と言い換えることで「大体同じなら等しい」とします.つまり,$| a - b | < \epsilon$ かどうかを見ます.

abs( a - b ) < EPS

大小比較

 2 つの実数 $a, b$ の大小関係を知りたいとします.こちらも,$a \leq b$ や $a < b$ のようにそのまま書いてしまうと壊れる可能性があります.こちらは適切な方に $\epsilon$ を足し引きして調整します.
 $\leq$ を判定する場合は,値が十分近いときに誤差のせいで順序が入れ替わってしまっても $\epsilon$ 分余裕をもたせる気持ちで $a - \epsilon \leq b$ か $a \leq b + \epsilon$ とします.これで,数学的には $a \leq b$ だけど浮動小数点数の値としては $a > b$ のときでも $b < a \leq b + \epsilon$ ぐらいになってくれるといい感じの $\epsilon$ なら上手くいきます.

a - EPS <= b
a <= b + EPS

 $<$ を判定する場合は,$\epsilon$ だけ近づけても $<$ が成り立つ,といった気持ちで $\epsilon$ をつけると $\leq$ のときと符号が逆になって,$a + \epsilon < b$ や $a < b - \epsilon$ となります.

a + EPS < b
a < b - EPS

 コードの上で < と <= を書き分ける必要はありませんが,わたしは,その方が EPS の付け方が分かりやすいので書き分けています(やりたい演算をまず書いて,後から EPS を書く).

おわりに

 まとめてみたはいいものの,最近使った記憶もあんまり無いような……?