OpenDogs extension 0.3.0 - PHP で複素数演算

php_opendogs-0.3.0.tgz
C99 の complex.h を使った複素数のサポートを追加しました。
倍精度実数の精度に制限されるので厳密さが求められる用途には厳しいかもしれません。
complex リソースで複素数の演算をします。(下記 API 一覧の返値/引数型では単に complex としていますが、正確には resource complex です)

complex od_c_new(float $real, float $imag)
実数部 $real, 虚数部 $imag の複素数を生成する
complex od_c_i([float $imag])
実数部 0.0, 虚数部 $imag の複素数を生成する。引数が省略された場合、1i を返す
complex od_c_{add,sub,mul,div}(complex $x, complex $y)
四則演算
complex od_c_{eq,ne}(complex $x, complex $y)
比較
complex od_c_{sin,cos,tan}(complex $z)
三角関数
complex od_c_a{sin,cos,tan}(complex $z)
三角関数
complex od_c_{sin,cos,tan}h(complex $z)
双曲線関数
complex od_c_a{sin,cos,tan}h(complex $z)
双曲線関数
complex od_c_exp(complex $z)
自然対数の底 e の $z 乗を求める
complex od_c_log(complex $z[, complex $base])
対数を求める。底が省略された場合、自然対数をとる
float od_c_abs(complex $z)
絶対値を求める
complex od_c_pow(complex $base, complex $exp)
べき乗を求める
complex od_c_sqrt(complex $z)
平方根を求める
complex od_c_conj(complex $z)
共役複素数を求める
complex od_c_proj(complex $z)
リーマン球への射影を求める
float od_c_arg(complex $z)
位相角度を求める
float od_c_real(complex $z)
実数部を得る
float od_c_imag(complex $z)
虚数部を得る
complex od_c_rotate(complex $z, float $arg)
複素平面上で $arg ラジアン回転する
float od_c_crossprod(complex $x, complex $y)
複素数を二次元ベクトルとみなし、外積を求める
float od_c_dotprod(complex $x, complex $y)
複素数を二次元ベクトルとみなし、内積を求める
array od_c_extract(complex $z)
実数部と虚数部のペアを得る
string od_c_str(complex $z)
文字列表記を得る

これら複素数を引数にとる関数に実数または整数が与えられた場合は、内部的に複素数に変換して演算します。数値文字列には対応しません。


例としてオイラーの公式を試してみます。

<?php
$theta = 12.3;
$left  = od_c_exp(od_c_i($theta));
$right = od_c_new(cos($theta), sin($theta));
var_dump(od_c_eq($left, $right));
bool(true)

ばっちりですね。
では Θ=Π のとき ― オイラーの等式を試してみましょう。

<?php
$z = od_c_exp(od_c_i(M_PI));
var_dump(od_c_eq($z, -1));
bool(false)

想定外の事態です。$z の中身を見てみましょう。

<?php
$z = od_c_exp(od_c_i(M_PI));
var_dump(od_c_extract($z));
array(2) {
  [0]=>
  float(-1)
  [1]=>
  float(1.2246063538224E-16)
}

虚数部が 0 ではありませんね。人類の至宝が台無しです。
これは浮動小数点演算の誤差以前に、M_E と M_PI がそれぞれネイピア数と円周率を double で表現可能な範囲に丸めてあるためですが、このあたりに C 系の言語 (の標準ライブラリ) の限界を感じてしまいます。


それはさておき、リソース型を使って複素数を実現するよりは、できることなら言語仕様に組み込みたいものです。
必要そうなことをざっと挙げてみると、

  1. zval.value 共用体のメンバに complex double cval を追加する
  2. 定数 IS_COMPLEX を追加し、zend_get_type_by_const(IS_COMPLEX) が "complex" を返すようにする
  3. マクロ Z_CVAL(_P,_PP) を実装する
  4. 引数の型指定子 c のサポートを zend_parse_paramters 系 API に実装する
  5. 型の強制変換 API convert_to_complex(_ex) を実装する
  6. 数学関数で複素数を扱えるようにする
  7. 算術演算子複素数を扱えるようにする
  8. 構文解析器で複素数リテラルをパースできるようにする

この他にも不特定の型を扱う API および関数すべてに手を入れる必要があります。
考えてみただけで実際にはやらないんですけどね。