C++標準ライブラリの数学定数への道のり

C++20で数学定数が入ることが決まりましたね。やっと標準ライブラリの範囲で円周率を定数として使えるようになります。

ここまでの道のりですが、

  1. constexpr (C++11)
  2. 変数テンプレート (C++14)
  3. インライン変数 (C++17)
  4. 数学定数 (C++20)

とても長かったですね。

#include <iostream>
#include <numbers>

template <class T>
T degree_to_radian(T x)
{
    return x * std::numbers::pi_v<T> / static_cast<T>(180.0);
}

int main()
{
    float y = degree_to_radian(90.0f);
    std::cout << y << std::endl; // 1.5708
}

constexpr (C++11)

汎用的なコンパイル時計算ができるようになりました。

変数テンプレート (C++14)

constexpr関数では毎回値を計算することになるので、計算済みの値を変数として保持しておくために、テンプレートで変数定義ができるようになりました。

インライン変数 (C++17)

ヘッダファイルで変数を定義してもODR違反にならないよう、翻訳単位を跨いで実体をひとつにするために、インライン変数が導入されました。

これら全てを組み合わせてできた数学定数

// <numbers>ヘッダ
namespace std::numbers {
    // 先行宣言
    template<typename T> inline constexpr T pi_v;

    // 浮動小数点数だけ特殊化して定義
    template <FloatingPoint T>
    inline constexpr T pi_v<T> = (...計算...);

    // デフォルトでdouble
    inline constexpr double pi = pi_v<double>;
}

コンセプトも使われていますが、最終的にこうなりました。

std::numbers::piを使うとdouble型になり、std::numbers::pi_v<T>を使うと任意の浮動小数点数型になります。

非標準のM_PIマクロは昔からありましたが、これからは標準の数学定数が使えます。コンパイラが実装したらね!

次は数学関数のconstexpr化ですね。