Compile time computing of number of bits needed to encode n different states

淺唱寂寞╮ 提交于 2019-11-30 18:55:12

The minimum number of bits required to store n different states is ceil(log2(n)).

constexpr unsigned floorlog2(unsigned x)
{
    return x == 1 ? 0 : 1+floorlog2(x >> 1);
}

constexpr unsigned ceillog2(unsigned x)
{
    return x == 1 ? 0 : floorlog2(x - 1) + 1;
}

Note that ceillog2(1) == 0. This perfectly fine, because if you want to serialize an object, and you know that one of its data members can only take on the value 42, you don't need to store anything for this member. Just assign 42 on deserialization.

Try this:

constexpr unsigned numberOfBits(unsigned x)
{
    return x < 2 ? x : 1+numberOfBits(x >> 1);
}

Simpler expression, correct result.

EDIT: "correct result" as in "the proposed algorithm doesn't even come close"; of course, I'm computing the "number of bits to represent value x"; subtract 1 from the argument if you want to know how many bits to count from 0 to x-1. To represent 1024 you need 11 bits, to count from 0 to 1023 (1024 states) you need 10.

EDIT 2: renamed the function to avoid confusion.

maybe

constexpr int mylog(int n) {
    return (n<2) ?1:
           (n<4) ?2:
           (n<8) ?3:
           (n<16)?4:
           (n<32)?5:
           (n<64)?6:
           …
           ;
}

as you will use it as tempalte parameter you might want to check out what boost has to offer

constexpr is a bit underpowered and will be until C++14. I recommend templates:

template<unsigned n> struct IntLog2;
template<> struct IntLog2<1> { enum { value = 1 }; };

template<unsigned n> struct IntLog2 {
private:
  typedef IntLog2<n - 1> p;
public:
  enum { value = p::value * p::value == n - 1 ? p::value + 1 : p::value };
};
bolov

Due to the confusion caused by the initial question I chose to post this answer. This is built upon the answers of @DanielKO and @Henrik.

The minimum number of bits needed to encode n different states:

constexpr unsigned bitsNeeded(unsigned n) {
  return n <= 1 ? 0 : 1 + bitsNeeded((n + 1) / 2);
}

Something I've used in my own code:

static inline constexpr
uint_fast8_t log2ceil (uint32_t value)
/* Computes the ceiling of log_2(value) */
{
    if (value >= 2)
    {
        uint32_t mask = 0x80000000;
        uint_fast8_t result = 32;
        value = value - 1;

        while (mask != 0) {
            if (value & mask)
                return result;
            mask >>= 1;
            --result;
        }
    }
    return 0;
}

It requires C++14 to be used as constexpr, but it has the nice property that it's reasonably fast at run time—about an order of magnitude faster than using std::log and std::ceil—and I've verified that it produces the same results for all representable non-zero values (log is undefined on zero, though 0 is a reasonable result for this application; you don't need any bits to distinguish zero values) using the following program:

#include <iostream>
#include <cstdlib>
#include <cstdint>
#include <cmath>
#include "log2ceil.hh"

using namespace std;

int main ()
{
    for (uint32_t i = 1; i; ++i)
    {
        // If auto is used, stupid things happen if std::uint_fast8_t
        // is a typedef for unsigned char
        int l2c_math = ceil (log (i) / log (2));
        int l2c_mine = log2ceil (i);
        if (l2c_mine != l2c_math)
        {
            cerr << "Incorrect result for " << i << ": cmath gives "
                 << l2c_math << "; mine gives " << l2c_mine << endl;
            return EXIT_FAILURE;
        }
    }

    cout << "All results are as correct as those given by ceil/log." << endl;
    return EXIT_SUCCESS;
}

This shouldn't be too hard to generalize to different argument widths, either.

In C++20, we have (in the header <bit>):

template<class T>
  constexpr T log2p1(T x) noexcept;

Returns: If x == 0, 0; otherwise one plus the base-2 logarithm of x, with any fractional part discarded. Remarks: This function shall not participate in overload resolution unless T is an unsigned integer type.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!