expm1 や pow などの 指数・対数函数 への考察
小清水 (@curekoshimizu) です。
今日は の 乗、
について考えてみたいと思います。
この定義ってどのように習いましたでしょうか?
と を定義した後に
により定義していたかと思います。
この記事ではいったん複素数のことは忘れましょう。
多価関数が...とか言い出すと本質からずれますので。
この定義を考えれば
をコンピューターで計算した値 と
をコンピューターで計算した値 は
同じ計算結果になるということなのでしょうか。
こうしたことを調べるために、
そもそも と などを
どのようにコンピューターで計算するのか
ということを考える必要があります。
ここではプログラミング言語が
ライブラリとして標準的に提供している函数 を使い
計算していみることにしましょう。
そのため、まずはライブラリで提供されている函数を調べることにします。
Outline
1. C言語 の標準ライブラリで用意されている 指数・対数函数まとめ
2. 他のプログラミング言語 と比較
3. と の計算結果比較
1. C言語 の標準ライブラリで用意されている 指数・対数函数まとめ
math.h で公開されている 指数函数・対数函数をまとめてみましょう。
指数函数シリーズ
Cの標準ライブラリには 指数函数 を計算する標準函数シリーズとして
を計算する exp2
を計算する exp
を計算する expm1
を計算する pow
があります。
対数函数シリーズ
Cの標準ライブラリには 対数函数 を計算する標準函数シリーズとして
を計算する log2
を計算する log10
を計算する log
を計算する log1p
考察してみる
Taylor展開結果 をご存知の方は
だけでなく
が用意されていることや
だけでなく
が用意されている
理由がわかるかと思います。
これは使い分けなくてはいけないのですか?
が非常に小さいときは 当然 です!
#include <stdio.h> #include <math.h> int main(void) { const double x = exp2(-100); printf("%e\n", exp(x) - 1.0); printf("%e\n", expm1(x)); return 0; }
の実行結果は
0.000000e+00 7.888609e-31
となり の exp(x) - 1 と expm1(x) の計算結果は異なることがわかります。
また、興味深いとおもったことは
指数函数 には が提供されていない 点です。
これは私の調査ミスでしょうか?理由をご存じの方いらっしゃいましたら教えてください。
もちろん を使えば が計算できますが、
それではなぜ 系のみ存在するのでしょう。
もう一点気になるのは が提供されているにも関わらず
が提供されていないのか ということです。
2. 他のプログラミング言語と比較
上では C言語について調査してみたわけですが
他の言語ではどのようになっているのでしょうか。
例として python・ruby・golang について調べてみました。
注意として
例えば 函数が標準ライブラリで提供されていない場合、
それは で代用できるわけですが 代用可能(△) ということにしました。
特に ruby は x ** y
という記法で を表しますが、標準ライブラリには函数が存在しないので (○) としました。
p 2.72 ** 3.14
python もこの記法が使えますが pow 函数が提供されていましたので ○ としています。
また golang には Pow10 という函数があるのですが、
整数値限定であり実数用ではないため除外しています。
C | python | ruby | golang | |
---|---|---|---|---|
○ | △ | △ | ○ | |
△ | △ | △ | △ | |
○ | ○ | ○ | ○ | |
○ | ○ | × | ○ | |
○ | ○ | (○) | ○ | |
○ | ○ | ○ | ○ | |
○ | ○ | ○ | ○ | |
○ | ○ | ○ | ○ | |
○ | ○ | × | ○ | |
× | ○ | ○ | × |
このように差があると思っていませんでしたので、
間違いだよというのがありましたらご指摘ください。
調査につかったページリンク
- python : http://docs.python.jp/2/library/math.html
- ruby : https://docs.ruby-lang.org/ja/latest/class/Math.html
- golang : https://golang.org/pkg/math/
ここからわかる面白そうなこと
ruby に と がないので
この計算が必要とする場合、精度で問題になる例がつくれるかもしれない。
この計算が必要とする場合、精度で問題になる例がつくれるかもしれない。
3. と の計算結果比較
もともとこの 2つを計算して比べてみようという話でした。
ここでは
を調べてみます。
pow(3, 559) の計算結果 (倍精度) は
513784961483922578512339180019698197951798048111926076589406942445158467436810000901599831502922238040737504312146764722557085503242923817731646777028029088263023249828259097129213987023170862839861791282352675466344683968308962273231524580225440726788249796697128960
pow(559 * log(3) ) の計算結果 (倍精度) は
513784961483977278818823467195598637829111367053443355111225788391489700605741415033850055549866587533685229352343834799219693792708265915735692787177526938262762638175334276719142227318286192430271216131815303492760406601731641300588913449010146062071623162601144320
すなわち
誤差 = 54700306484287175900439877313318941517278521818845946331233168931414132250224046944349492947725040197070076662608289465342098004046010149497849999739388347075179589928240295115329590409424849462628026415722633422679027357388868784705335283373365904015360
が生じています。
とんでもない大きさの誤差が生じてしまいました!
考察
の値が 浮動小数点演算により 近似されたことにより、
次の exp 函数へ誤差が伝播したことが原因と考えられます。
ということは
という公式がありますが、C言語や golang のように標準ライブラリがないものは
このような公式で計算してしまうと
多大な誤差を生む可能性がある ということを意味しているのかもしれません。
上で挙げた expm1 などもそうですが、
やはり標準ライブラリとして一つにまとまっている函数に対しては
丸め誤差への安心感が違います!
そんなお話でした。