[英]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
不同狀態所需的最小位數,即整數部分 (向下舍入) 二進制對數的四舍五入:
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::log
和std::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.