読者です 読者をやめる 読者になる 読者になる

constexprな関数でコンパイル時にn乗根を求める

とあるプログラムで,定数の3乗根を求めたいことがありました.
どうせ求めるならこれからも使えそうなn乗根で,どうせならconstexprでということで,書いてみました.

求め方としてはニュートン法を使っています.初期値さえ間違えなければニュートン法はかなり収束が速いはずなので,デフォルトのコンパイル時の最大再帰回数(512回)以内には終わると思います.

#include <cstddef>

using std::size_t;

namespace detail {

    constexpr double power(double base, size_t n)
    {
        return n==0 ? 1 : base * power(base, n - 1);
    }

    constexpr double abs(double value)
    {
        return value>0.0 ? value : -value;
    }


    constexpr double nth_root_impl(double value, 
                                                         size_t n,
                                                         double base,
                                                         double epsilon)
    {
        return abs( value - power(base,n) ) < epsilon ?
                   base : nth_root_impl( value, n, ((n-1) * base + value / power(base, n-1)) / n, epsilon );
    }

}

constexpr double nth_root(double value, size_t n)
{
    // x0=1.0,epsilon=1e-7
    return detail::nth_root_impl(value, n, 1.0, 1e-7);
}


#define CHECK(v,n) static_assert( detail::abs( detail::power( nth_root( v, n ), n ) - v ) < 1e-7, "")

int main()
{
    CHECK(8816, 44);
    CHECK(366, 31);
    CHECK(3643, 73);
    CHECK(51, 8);
    CHECK(2592, 79);
    CHECK(4996, 67);
    CHECK(947, 6);
    CHECK(39, 60);
    CHECK(2940, 5);
    CHECK(673, 2);
    CHECK(182, 21);

    return 0;
}

constexprな関数は従来のコンパイル時処理またはコンパイル前処理に比べて書きやすくて良いですね.