💡 この記事は「コンピューターで計算する」シリーズの一部です。
1. 浮動小数点数
2. 区間演算👈 今ここ
3. 数値積分
4. 数値微分
5. 自動微分
浮動小数点数で計算をしていると、「どこまでが正しい値なのか?」 という不安が常につきまといます。
丸め誤差は1回の計算では小さくても、1,000回、1,000,000回と繰り返されるうちに無視できない大きさになっていきます。
計算結果をひとつの値として扱う従来のアプローチでは、丸め誤差が累積してしまう問題があります。そこで役立つのが、区間演算(Interval Arithmetic) です。
計算結果を「ひとつの値」ではなく、真値を必ず含む範囲(区間) として扱うことで、厳密に正しい値を計算します。
この記事では、区間演算の仕組みと、実際に利用できる区間演算ライブラリを紹介します。
区間演算(Interval arithmetic)
区間演算とは、数値を1つの値ではなく「その値が存在しうる範囲(区間)」として扱う計算手法です。
浮動小数点数では、丸め誤差によって計算結果が真の値からずれてしまう可能性があります。
1回の演算では小さな誤差に感じるかもしませんが、丸め誤差が発生する演算を
- 1,000回
- 10,000回
- 1,000,000回
と繰り返されると、丸め誤差が蓄積して無視できない大きさに達することがあります。
そこで、実数$x$を1点で近似するのではなく
機械的に表現できる浮動小数点数を端点にもつ閉区間
$$ \begin{aligned} X = [a,b] \end{aligned} $$
として表現します。
これは「実数$x$は必ず$a$以上$b$以下のどこかにある」という形にすることで厳密に正しいものにしています。
$$ \begin{aligned} a \le x \le b \end{aligned} $$
丸め誤差を無視することなく、むしろ丸め誤差ごと包み込む形で表現するのが区間演算の特徴です。
区間演算の種類
区間演算には大きく分けて上端下端型(inf-sup) と中心半径型(mid-rad) の2種類があります。
1. 上端下端型(inf-sup 型 interval = [inf, sup])
数値を「下端(inf)」と「上端(sup)」のペアで直接表現する方法です。
$$ \begin{aligned} X = [ inf , sup ] \end{aligned} $$
上端下端型の区間演算では
- 下向き丸め
下端を計算 - 上向き丸め
上端を計算
のように計算します。
これにより、丸め誤差があっても必ず"真の値を含む区間"を保証できるのが最大の利点です。
- 上端下端型(inf-sup)
数値を[下端,上端]のペアで表現。
下端を計算するときは下向き丸め、上端を計算するときは上向き丸めで計算することで、丸めの影響を把握して真の値を区間内に包含する。 - 中心半径型(mid-rad)
数値を[中心点,半径]で表現
$mid \pm rad$のように区間を表現する。
2. 中心半径型(mid-rad 型 interval = [mid, rad])
数値を「中心(mid)」と「半径(rad)」で表現します。
$$ \begin{aligned} X = mid \pm rad \end{aligned} $$
つまり区間は
$$ \begin{aligned} X = [mid - rad , mid + rad] \end{aligned} $$
と解釈できます。
区間演算による精度保証
浮動小数点数で計算していると
「この結果は何桁目まで信じてよいのか?」
という疑問が常につきまといます。
通常の計算では、丸め誤差がどのように蓄積したのかを知ることができないため、結果のどこまでが正しい値なのかを判断できません。
そこで役に立つのが区間演算(Interval Arithmetic) です。
例として$\pi$を区間で表現してみましょう。
$$ \begin{aligned} \pi = [3.1415 , 3.1416] \end{aligned} $$
これは次の意味を表します。
$$ \begin{aligned} 3.1415 \le \pi \le 3.1416 \end{aligned} $$
ここで、区間の両端で一致している桁を見ると
$$ \begin{aligned} {\color{red}3.141}5 \le \pi \le {\color{red}3.141}6 \end{aligned} $$
赤文字の${\color{red}3.141}$の桁は区間の両端で同じ値であるため、この4桁までは必ず正しい(精度が保証されている) ことがわかります。
区間演算ライブラリの紹介
区間演算を実際に扱うためのライブラリは、上端下端型(inf-sup 型) と 中心半径型(mid-rad 型) に分かれています。
それぞれで代表的なライブラリを紹介します。
上端下端型(Inf–Sup Interval)
- kv ライブラリ(C++)
C++ で実装された精度保証付き数値計算ライブラリです。
上端下端(inf–sup)で区間を扱うため、丸め方向を明示的に制御しながら区間を計算できます。
使用には Boost が必要です。
👉 kvライブラリ公式サイト
中心半径型(Mid–Rad Interval)
Arb ライブラリ(C言語 / FLINT 統合)
C/C++で使える高速な多倍長計算/中心半径型の区間演算ライブラリです。
特殊関数の高精度計算にも用いられています。
現在はFLINTに統合されたようです。
Linux / macOS で動作します。
👉 Arb Library SetupPython-FLINT(Python バインディング)
Arb / FLINT を Python から利用できる拡張モジュールです。
Windows でも動作し、Python で高精度かつ精度保証付きの計算を行いたい場合に最適です。
👉 Python-FLINT Setup
Python-FLINTの導入(Windows)
現在の筆者の環境がwindowsな為、ここではPython FLINTの導入をしていきます。
他のOSを使っている方はArbライブラリが統合されたFLINTやkvライブラリを導入すると良いです。
モジュールのインストール
py -m pip install python-flint
次のようにプログラムを作成
from flint import arb
x = arb.pi()
print(x)
実行結果
[3.14159265358979 +/- 3.34e-15]
これは、次のような意味になっています。
$$ \begin{aligned} 3.14159265358979 \pm (3.34 \times 10^{-15}) \end{aligned} $$
範囲で表すと
$$ \begin{aligned} 3.14159265358979 - (3.34 \times 10^{-15}) \le \pi \le 3.14159265358979 + (3.34 \times 10^{-15}) \end{aligned} $$
です。
精度の変更
精度が保証された値をさらに高精度に求めるために、精度の変更を行っていきます。
1. 精度をビット数(2進数)で指定する。
プログラムを次のように変更します。
ビットで精度を指定したい時はctx.precを使用します。
from flint import arb, ctx
# 精度を106ビットに設定(10進で約32桁の精度)
ctx.prec = 106
x = arb.pi()
print(x)
106bitの精度は、10進数では次のように換算されます。
$$ \begin{aligned} 106\times \log_{10}(2) \approx 31.9 \end{aligned} $$
実行結果
[3.141592653589793238462643383279 +/- 5.06e-31]
2. 精度を10進数で指定する。
プログラムを次のように変更します。
10進数で精度を指定したいときはctx.dpsを使用します。
10進で50桁程度の精度に設定します。
from flint import arb, ctx
# 10進でおよそ50桁分の精度
ctx.dps = 50
x = arb.pi()
print(x)
実行結果
[3.1415926535897932384626433832795028841971693993751 +/- 9.51e-51]
精度保証されている桁だけを出力する
showgood関数を使うと、midのうちradiusの影響を受けない
「区間の上下端が一致している桁(=good digits,精度保証値)」 だけを表示出来ます。
Arbの区間表示は
[mid +/- rad]
という形ですが、これは数学的には次の不等式を表しています。
$$ \begin{aligned} mid - rad \le x \le mid + rad \end{aligned} $$
ここで
- 上端
$$ \begin{aligned} hi = mid + rad \end{aligned} $$
- 下端
$$ \begin{aligned} lo = mid - rad \end{aligned} $$
とすると$hi$と$lo$を並べてみたときに、先頭から共通している桁は区間内のどんな真の値$x$を取っても変わらない桁になります。
showgoodはまさにこの
- 上端$hi$と下端$lo$が一致している部分だけを抜き出した表示
- つまり「精度が保証されている桁」だけを抜き出したmidの表現
を出力する関数です。
プログラムを次のように変更します。
from flint import arb, ctx, showgood
# 計算精度を50桁に設定
ctx.dps = 50
x = arb.pi()
print("通常の出力:")
print(x)
print("showgoodによる出力:")
showgood(lambda: arb.pi())
# ctx.dps を指定していれば、dps=50は省略しても良い。
# showgood(lambda: arb.pi(), dps=50)
実行結果
通常の出力:
[3.1415926535897932384626433832795028841971693993751 +/- 9.51e-51]
showgoodによる出力:
3.1415926535897932384626433832795028841971693993751
※環境によっては
3.1415926535897932384626433832795028841971693993751xxxx
のように、不確かな桁がxでマスクされる形式で出力される場合もあります。
実行例のようにshowgoodは、区間演算の結果から、確実に正しいとされる桁のみを表示する機能です。
この機能を使うことで、計算結果にどの程度の信頼性があるかを簡単に確認できます。
他にも、Python-FLINTモジュールには様々な機能があります。
arbは実数の精度保証付きの数値計算に使用され、acbは虚数を含む計算を行いたい場合に使用します。虚数の精度保証付きの数値計算が必要な場合に、acbを使用することで、複素数計算を高精度に行えます。
さらに詳しい使い方やAPIについては👉 Python-FLINT リファレンス をご覧ください。
まとめ
本記事では区間演算(Interval Arithmetic) についての紹介と
実際に使える区間演算ライブラリ(kv / Arb / Python-FLINT)を紹介しました。
浮動小数点数は、本質的に
- 有効桁が有限
- 計算量が増えるほど丸め誤差が蓄積する
- 扱える値の範囲が有限
といったような制約を持っています。
それに対し、区間演算は
- 「真値が必ず含まれる範囲」を計算する
- 丸め誤差ごと安全に包み込む
- 結果のどこまでが信頼できるか(精度保証)が得られる
という特徴を備えています。
ここで紹介したArb / Python-FLINTの中心半径型(mid-rad) は高速・高精度 であり
数学計算・数値計算・誤差評価などで広く利用されています。
✔️ 本記事のポイント
- 区間演算では、丸め誤差を避けるために、計算結果を単一の値ではなく、誤差を含んだ範囲(区間)で表現することで、真値を必ず包含している区間を計算します。
- 区間演算には上端下端(inf-sup)型と中心半径(mid-rad)型の2種類の方法がある。
- PythonではPython-FLINTが手軽で強力
showgoodを使うと保証された桁だけを取り出せる
✔️ 今後の記事では…
- 数値積分
- 数値微分
- 精度保証付きの自動微分
などを紹介します。