nptclのブログ

Common Lisp処理系nptの開発メモです。https://github.com/nptcl/npt

C言語にて関数csinlが使えない場合がある

数学のsin関数に複素数って渡せるんですね。 全然知りませんでした。

C言語のc99標準ライブラリにも用意されているようです。
具体的には下記の関数。

csinf, csin, csinl, sin(tgmath)

問題は、clangのlong double complexを引数とする関数がいくつか使えない事です。 例えばclangではcsinlを使おうとするとエラーになります。 clangというとFreeBSDの標準コンパイラですが、 manコマンドのマニュアルにも載ってなようなので、 少なくともFreeBSD開発側は認識できているようです。
確認した環境は下記の通り。

% freebsd-version
12.0-RELEASE-p3
% clang --version
FreeBSD clang version 6.0.1 (tags/RELEASE_601/final 335540) (based on LLVM 6.0.1)
Target: x86_64-unknown-freebsd12.0
Thread model: posix
InstalledDir: /usr/bin

モジュール自体が存在しないので、csinlがあるとコンパイルが通りません。 tgmath.hを利用してsin関数にlong double complexを渡しても、 あれは結局マクロでcsinlにするだけなのでエラーになります。

そうなんですか、無理ですか。
完全に他人任せで実装されるまで大人しく待ってようと思います。

しかし妥協すれば解決策はいくつかあると思います。
私の大のお気に入りは、下記の極めて最低な方法。

#ifdef __clang__
#define csinl csin
#endif

他には、C言語をあきらめて、C++を利用する方法。

#include <stdio.h>
#include <cmath>
#include <complex>

int main()
{
    std::complex<float> f(1.2f, 3.4f);
    std::complex<double> d(1.2, 3.4);
    std::complex<long double> l(1.2L, 3.4L);

    f = std::sin(f);
    d = std::sin(d);
    l = std::sin(l);

    printf("%30.25e, %30.25e\n", real(f), imag(f));
    printf("%30.25e, %30.25e\n", real(d), imag(d));
    printf("%30.25Le, %30.25Le\n", real(l), imag(l));

    return 0;
}

実行結果

$ clang++ test.cpp
$ ./a.out
1.3979410171508789062500000e+01, 5.4228153228759765625000000e+00
1.3979408806017994848502894e+01, 5.4228154724634016758955113e+00
1.3979408806017997938912767e+01, 5.4228154724634012448167275e+00

うまく行っているように見えます。
なぜC++でうまく行ってC99じゃダメなのか。

他には、clangではなくgccを利用する方法。
2月くらいのGentoo Linuxで確認しましたが、こちらは問題ありません。

そしてあまりお勧めできないのは自分でcsinlを実装する方法です。
正直csinl程度なら何とかなりそうなもんですけど、 arcsinやらになると、数値計算に自信がないならやめた方がいいかもしれません。

もしかして自分が勘違いしているだけでcsinlはc99標準じゃない? 関数clogなんて丸ごと存在しないんですよね。 わざわざ/usr/includeを確認してしまった。