0

I'm coding a function that evaluates binomial coefficients: binom(N, K)

I want to check that both N and K are unsigned-type integers with N >= K at compile time, but having a problem with this.

Here's my code:

template <typename N>
concept Unsigned = std::is_unsigned_v<N>;

template <Unsigned U>
constexpr double binom(U N, U K) {
    static_assert(N >= K);
    double d = 1.0;
    while (K) {
        d *= static_cast<double>(N) / static_cast<double>(K);
        K--;
        N--;
    }
    return d;
}

template <Unsigned U>
constexpr double binom_pmf(U N, U K, double theta) {
    return binom(N, K) * std::pow(theta, K) * std::pow(1 - theta, N - K);
}

Here is the error message from Clang 10:


/mnt/c/programming/ML/2_3_1_binomial_bernoulli.cpp:12:19: error: static_assert expression is not an integral constant expression
    static_assert(N >= K);
                  ^~~~~~
/mnt/c/programming/ML/2_3_1_binomial_bernoulli.cpp:24:12: note: in instantiation of function template specialization 'binom<unsigned long>' requested here
    return binom(N, K) * std::pow(theta, K) * std::pow(1 - theta, N - K);
           ^
/mnt/c/programming/ML/2_3_1_binomial_bernoulli.cpp:36:16: note: in instantiation of function template specialization 'binom_pmf<unsigned long>' requested here
        y[K] = binom_pmf(N, K, theta);

The problem is static_assert(N >= K).

I've declared parameter N as constexpr size_t, and K is the loop parameter in for (size_t K = 0; K <= N; K++), so anyone can conclude that N and K are surely unsigned integers with N >= K, but the compiler doesn't look so happy.

Oh, the compiler is also complaining when I try to insert static_assert(theta >= 0.0 && theta <= 1.0); in binom_pdf.

What should I do? Thanks in advance.

frozenca
  • 839
  • 4
  • 14

2 Answers2

1

Function parameters are not constexpr.

template <Unsigned U, U N, U K> constexpr double binom() would allow your static_assert.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thank you very much! But my attempt to ````template constexpr double binom_pmf() {```` is still failing. How can I pass floating-point types as template parameters? – frozenca Mar 31 '20 at 11:22
  • 1
    `double` cannot be template parameter (at least in C++17). C++20 should allow that though (see [template_parameters](https://en.cppreference.com/w/cpp/language/template_parameters)). C++20 is not fully supported by compilers. – Jarod42 Mar 31 '20 at 11:25
0

If you really are happy with all these being available only for constant expressions, declare the functions consteval and throw (no actual exception needed) when you don’t like the arguments. That allows floating-point types even without compiler support for them as template parameters.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76