普段はWindows向け関数電卓ソフト「S-Calc」を個人開発しています。
今回、負の値を底に持つ対数log(-5)
が計算できない問題に直面しました。
原因は内部で利用している数式エンジンxFuncが負の値の対数をサポートしていないためです。
この記事では、どのようにして「負の値を含む対数」をサポートしたか、
そしてC#.NETのSystem.Numerics.Complex.Log
を活用した実装方法について簡単にまとめます。
まず、C#.NETのMath.Log10
は引数に負の数を渡すとNaNを返します。
また、xFuncは実数ドメインのみの対応なので、複素数ドメインの対数が計算できません。
これではS-Calc
の関数電卓としての有用性が失われてしまうと考え、以下の解決策を考えました。
解決策
- 正規表現で
log(base, value)
を先に拾う
const string pattern = @"(?i)\blog\((?<base>[^,]+),(?<val>[^\)]+)\)";
var match = Regex.Match(expr, pattern);
if (match.Success)
{
var b = EvalAsComplex(match.Groups["base"].Value);
var v = EvalAsComplex(match.Groups["val"].Value);
var result = Complex.Log(v, b.Real);
expr = expr.Replace(match.Value, ToLiteral(result));
}
※実際に実装したソースとは異なりますが、このような形です
- C#.NETのComplex.Log(value, base)で拾った対数関数を計算する
- 計算結果を複素数である
"a+bi"
のカタチに整形して元の式に差し替え、xFunc.Mathに渡す
これらを実装する際、以下のような工夫をしました。
- 入れ子の
log(log(...))
にも対応できるよう再帰的に処理 -
gamma()
など他の関数が入っていても再計算できるようにした
その結果、以下のようなケースのテストを網羅し実装を終えました。
- log(10,-5) → 0.69897 + 1.36437i
- log(e,-1) → 3.1419i
- log(2,√(-9)) → 1.585 + 2.2662i
※ Shiftボタンを押下することで関数ボタンの切り替えが可能です
結び
複素数対応は「ニッチ」ではありますが、
理系ユーザにとっては「普通の電卓では絶対に出せない解」を与えらえる差別化ポイントになりました。
実際に動作する関数電卓「S-Calc」はMicrosoft Storeで公開しています。
https://apps.microsoft.com/detail/9n2gm51phq7p?hl=ja-jp&gl=JP