编辑:在最初的问题中有一个错误的公式,并且尝试的算法正在做一些与预期完全不同的事情。我道歉,我决定重写这个问题以消除所有困惑。
我需要在编译时计算 (结果将用作非类型模板参数)存储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,操作系统冻结,编译器崩溃)。
如何在编译时以实用的方式计算它?
答案 0 :(得分:6)
存储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
。
答案 1 :(得分:5)
试试这个:
constexpr unsigned numberOfBits(unsigned x)
{
return x < 2 ? x : 1+numberOfBits(x >> 1);
}
更简单的表达,correct result。
编辑:“正确的结果”,如“建议的算法甚至没有接近”;当然,我正在计算“代表值x的位数”;如果要知道从0到x-1计数的位数,请从参数中减去1。要表示1024,您需要11位,从0到1023(1024个状态)计数,您需要10。
编辑2 :重命名该功能以避免混淆。
答案 2 :(得分: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:
…
;
}
因为您将其用作模板参数,您可能需要查看boost has to offer
答案 3 :(得分:1)
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 };
};
答案 4 :(得分:1)
由于最初问题引起的混淆,我选择发布此答案。这是基于@DanielKO和@Henrik的答案。
编码n
个不同状态所需的最小位数:
constexpr unsigned bitsNeeded(unsigned n) {
return n <= 1 ? 0 : 1 + bitsNeeded((n + 1) / 2);
}
答案 5 :(得分:1)
我在自己的代码中使用过的东西:
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;
}
这不应该太难以概括为不同的参数宽度。
答案 6 :(得分:0)
在C ++ 20中,我们(位于标头SoftDeletes
中):
import Portal from '@material-ui/core/Portal';
const FooComponent = (props) => {
const portalRef = useRef(null);
return <>
<form>
First form
<div ref={portalRef} />
</form>
<Portal container={portalRef.current}>
<form>Another form here</form>
</Portal>
</>;
}
返回:如果x == 0,则为0;否则,加上x的以2为底的对数,并丢弃所有小数部分。 备注:除非T是无符号整数类型,否则此函数不得参与重载解析。