簡體   English   中英

編譯編碼n個不同狀態所需的比特數的時間計算

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

編輯:在最初的問題中有一個錯誤的公式,並且嘗試的算法正在做一些與預期完全不同的事情。 我道歉,我決定重寫這個問題,以消除所有的困惑。

我需要在編譯時計算(結果將用作非類型模板參數)存儲n不同狀態所需的最小位數:

constexpr unsigned bitsNeeded(unsigned n);

或通過模板

結果應該是:

+-----------+--------+
| number of | bits   |
| states    | needed |
+-----------+--------+
|     0     |    0   | * or not defined
|           |        |
|     1     |    0   |
|           |        |
|     2     |    1   |
|           |        |
|     3     |    2   |
|     4     |    2   |
|           |        |
|     5     |    3   |
|     6     |    3   |
|     7     |    3   |
|     8     |    3   |
|           |        |
|     9     |    4   |
|    10     |    4   |
|    ..     |   ..   |
|    16     |    4   |
|           |        |
|    17     |    5   |
|    18     |    5   |
|    ..     |   ..   |
+-----------+--------+

最初(以某種方式更正)版本供參考:

我需要在編譯時計算(結果將用作非類型模板參數)存儲n不同狀態所需的最小位數,即整數部分 (向下舍入) 二進制對數的四舍五入:

小區(LOG2(n))的

constexpr unsigned ceilLog2(unsigned n);

這就是我提出的( 完全錯誤的 ):

constexpr unsigned intLog2(unsigned num_states_) {
  return
    num_states_ == 1 ?
      1 :
      (
      intLog2(num_states_ - 1) * intLog2(num_states_ - 1) == num_states_ - 1 ?
        intLog2(num_states_ - 1) + 1 :
        intLog2(num_states_ - 1)
      );
}

這會產生正確的結果 (對於num_states_ != 0 ),但遞歸以指數方式吹出,對於任何大於10的輸入(編譯期間的內存使用量增長超過2GB,操作系統凍結且編譯器崩潰) 幾乎無法使用

如何在編譯時以實用的方式計算它?

存儲n不同狀態所需的最小位數是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;
}

注意ceillog2(1) == 0 這很好,因為如果你想序列化一個對象,並且你知道它的一個數據成員只能取值42 ,你不需要為這個成員存儲任何東西。 只需在反序列化時分配42

試試這個:

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

表達更簡單, 結果正確

編輯 :“正確的結果”,如“提出的算法甚至沒有接近”; 當然,我正在計算“代表值x的位數”; 如果要知道從0到x-1計數的位數,請從參數中減去1。 要表示1024,您需要11位,從0到1023(1024個狀態)計數,您需要10位。

編輯2 :重命名函數以避免混淆。

也許

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

因為您將它用作tempalte參數,您可能想要查看提升所提供的內容

constexpr有點動力不足,直到C ++ 14。 我推薦模板:

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 };
};

由於最初問題引起的混亂,我選擇發布這個答案。 這是建立在@DanielKO和@Henrik的答案之上的。

編碼n種不同狀態所需的最小位數:

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

我在自己的代碼中使用過的東西:

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;
}

它需要使用C ++ 14作為constexpr ,但它具有很好的屬性,它在運行時相當快 - 比使用std::logstd::ceil快一個數量級 - 我已經驗證了它為所有可表示的非零值生成相同的結果(log在零上未定義,但0對於此應用程序是合理的結果;您不需要任何位來區分零值)使用以下程序:

#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;
}

這也不應該太難以推廣到不同的參數寬度。

在C ++ 20中,我們(在標題<bit> ):

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

返回:如果x == 0,0; 否則加一個x的基數為2的對數,丟棄任何小數部分。 備注:除非T是無符號整數類型,否則此函數不應參與重載決策。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM