TUI電卓を作るというシリーズのlog実装回にて、本編のRustによる実装編では、さらっと「近似の式」として紹介した変数 \(z\) への置換。
\[\ln(m) = 2 \left( z + \frac{z^3}{3} + \frac{z^5}{5} + \dots \right)\]
「なぜ素直にマクローリン展開を使わないのか?」
「なぜ \(z\) にすると速いのか?」
これらについて、理論的な保証を含めて解説します。
本編 logの実装 : 【Rustで作る電卓】浮動小数点数の構造をハックして対数関数(log)を実装する #15
1. 素朴なマクローリン展開の限界
対数を級数で求めようとしたとき、最も有名なのは以下の \(\ln(1+x)\) のマクローリン展開でしょう。
(マクローリン展開自体の解説はここではしませんが,端的に言えば、微分を用いていい感じに多項式に分解し近似する方法のことです)
\[\ln(1+x) = x – \frac{x^2}{2} + \frac{x^3}{3} – \frac{x^4}{4} + \dots\]
一見シンプルで美しい式ですが、計算機に実装するには致命的な弱点があります。
- 収束が絶望的に遅い: \(x\) が \(1\) に近づくほど(つまり \(\ln(2)\) を求めようとするほど)、後の項の影響がなかなか小さくなりません。
- 交互級数の不安定さ: プラスとマイナスが交互に来るため、浮動小数点数では「情報落ち」のリスクが常に付きまといます。
計算機のlogの実装では、ビット演算で入力を \([1, 2)\) に絞り込みました。つまり \(x \in [0, 1)\) の範囲を計算することになりますが、この範囲でも上記の実装では \(f64\) の精度を出すのに数万回のループが必要になってしまいます。

普通のマクローリン展開でln(1.9)を求めたものをプロットした図
通常のマクローリン展開でln(1.9)を求めると、上記のグラフのようにプラスとマイナスの項を加算していくため、50項目まで計算したとしても結果は以下のようになり、小数第5位程度(f64の有効数字は最大17桁なのでとても大きい)誤差が残ってしまします。
※通常のマクローリン展開で50項目まで計算したときの結果
True Value: 0.6418538861723947
Value after 50 terms: 0.6418055742410455
Error: 4.8311931349243054e-05
2. 変数変換 \(z = \frac{m-1}{m+1}\) による改良
そこで、以下の変数変換を導入します。
\[m = \frac{1+z}{1-z} \iff z = \frac{m-1}{m+1}\]
この変換を \(\ln(m)\) に適用すると、対数の性質(割り算は引き算)から面白いことが起こります。
\[\ln(m) = \ln\left(\frac{1+z}{1-z}\right) = \ln(1+z) – \ln(1-z)\]
ここで、\(\ln(1+z)\) と \(\ln(1-z)\) それぞれを展開してみましょう。
- \(\ln(1+z) = z – \frac{z^2}{2} + \frac{z^3}{3} – \frac{z^4}{4} + \dots\)
- \(\ln(1-z) = -z – \frac{z^2}{2} – \frac{z^3}{3} – \frac{z^4}{4} – \dots\)
これらを引き算すると……偶数次の項がすべて打ち消し合って消滅します!マクローリン展開を習ったことがある人なら、sinとcosのテイラー展開からオイラーの公式を示すという有名な例で同じようなことをやったことがあるかもしれません。
\[\ln(m) = 2 \left( z + \frac{z^3}{3} + \frac{z^5}{5} + \frac{z^7}{7} + \dots \right)\]
置換によるメリット
- 計算量が半分: 偶数項がないため、同じ項数でも実質2倍の精度が得られます。
- 収束域の圧縮: 仮数部 \(m\) が \([1, 2)\) のとき、変換後の \(z\) は \([0, 1/3)\) という極めて狭い範囲に押し込められます。値が小さいほど、べき乗 (\(z^n\)) は猛烈な勢いで \(0\) に近づくため、収束が爆速になります。

z置換後の計算結果のプロットf64精度までの比較
error(誤差)が圧倒的な速さで減っていることがわかると思います。ちなみに、この展開はグレゴリー級数という対数関数の級数展開を利用している手法です。
(グレゴリー級数については、私は数学徒でも数学者でもないので詳しくないですが、検索してみると1671年と結構歴史が古く、円周率の計算に寄与していたりと案外面白かったです。)
【グレゴリー・ライプニッツ級数】微積分学が確立される前の無限への挑戦を紐解く
3. なぜ「12項」で十分なのか?(精度評価)
計算機の \(f64\)(倍精度)が保持できる有効数字は約 \(15 \sim 16 (17)\) 桁です。計算機で目指べきは、この精度を使い切ることです。
\(z\) の最大値である \(1/3\) を代入して、第 \(n\) 項の大きさを評価してみましょう。
第 \(n\) 番目の項は \(\frac{2}{2n+1} z^{2n+1}\) です。
\(n=12\)(つまり 12 回ループ)のときの項の値:
\[\text{Term}_{12} \approx \frac{2}{25} \left(\frac{1}{3}\right)^{25} \approx 0.08 \times 1.18 \times 10^{-12} \approx 9.4 \times 10^{-14}\]
これにホーナー法による累積誤差や前後の定数を考慮すると、12項程度で \(10^{-16}\) 近辺の精度、つまり \(f64\) の限界に近い精度に到達します。 実用上の TUI 電卓としては、これ以上回してもビットのゴミを計算するだけになり、かえって無駄になります。
ホーナー法とは、n次の式の結果を計算するときに、\(ax^n +bx^{n-1}+cx^{n-2}+\dots +αx+β\) を愚直に個別計算するのではなく以下のようにxの次数が大きいものから順次足し合わせながら計算することで掛け算の回数と丸め誤差を減らすという効率化手法のことです。
\[ax^n +bx^{n-1}+cx^{n-2}+\dots +αx+β \]
\[=x(\dots (x(x(ax +b)+c)) \dots + α) +β \]
4. 計算の実装例
理論をコードに落とし込むと、これだけコンパクトになります。
// 理論編:核心部の級数展開(ln_kernel)
fn ln_kernel(m: f64) -> f64 {
let z = (m - 1.0) / (m + 1.0);
let z2 = z * z;
let mut sum = 0.0;
// 第11項(i=11)から逆順にホーナー法を適用(ホーナー法については別記事で紹介)
for i in (0..12).rev() {
let denominator = (2 * i + 1) as f64;
sum = 1.0 / denominator + z2 * sum;
}
2.0 * z * sum
}ホーナー法: 関連記事は、2026年5月21日に公開予定 (あと16時間)
5. まとめ
「ただマクローリン展開をする」のと「変数変換して展開する」のでは、計算効率に天と地ほどの差が出ます。
- \(z\) 置換:偶数項を消去し、入力を \(1/3\) 以下に抑え込む。
- ホーナー法:べき乗計算を最小限に抑える。
- 誤差評価:\(f64\) の限界を見極めてループ回数を最適化する。
こうした数学的な裏付けがあって初めて、ビット演算ハックで得た「指数 \(k\)」と「仮数 \(m\)」が、一つの高精度な対数関数として完成します。
この理論を用いて Rust による電卓実装(本編)を実装しました。詳しいプログラムへの組み込みや、logの計算手法について紹介しています。
本編:Rustによる対数関数実装: 【Rustで作る電卓】浮動小数点数の構造をハックして対数関数(log)を実装する #15
ここまで読んでいただきありがとうございます。
では、次の記事で。 lumenHero
関連記事:計算プログラムの高速化手法いろいろ
第11回 平方根の計算実装:【Rust自作TUI電卓】mod.rsを活用したスマートなモジュール管理と基本数学関数の実装 #11
第13回 n乗根の計算実装:【Rust自作TUI電卓】ニュートン法×マジックナンバーで実装する汎用n乗根 #13
第14回 ハレー法による立方根:【Rust自作TUI電卓】ニュートン法を超えろ!ハレー法による立方根(cbrt)の極限最適化 #14
バビロニア法・ニュートン法:平方根の導出展開、手動でも解ける差分アプローチ【バビロニア法・ニュートン法】
高次の近似手法 ハウスホルダー法 : (記事は現在利用できません: ID 6925)
Rustによる対数関数実装: 【Rustで作る電卓】浮動小数点数の構造をハックして対数関数(log)を実装する #15
多項式計算の効率化
ホーナー法: 関連記事は、2026年5月21日に公開予定 (あと16時間)
エストリン法: 関連記事は、2026年5月22日に公開予定 (あと2日)
【グレゴリー・ライプニッツ級数】微積分学が確立される前の無限への挑戦を紐解く
微積分学の夜明け前、1671年に発見された『グレゴリー・ライプニッツ級数』。350年以上前に『無限』に挑んだ天才たちの足跡と、現代のRustにおける対数関数(ln)実装への意外な繋がりを、TUKUMO工房のLumenHeroが紐解きます。