编译时表示一个范围所需的位数

时间:2016-06-13 20:35:05

标签: c++ c++11 compile-time

我需要在编译时计算表示范围所需的位数。 对于从0到n的无符号范围,这很简单:

constexpr unsigned bits_to_represent(uintmax_t n) 
{
    return n > 0 
        ? 1 + bits_to_represent(n/2)
        : 0; 
}

对于签名范围,我有:

constexpr unsigned bits_in_range(intmax_t min,intmax_t max)
{
    return bits_to_represent(max >= 0
              ? static_cast<uintmax_t>(max) - min
              : max - min);
}

然而,这导致MSVC 2015(最近更新)抱怨:

警告C4308:负整数常量转换为无符号类型

你能解释一下为什么会这样吗?作为解决方法,我static_cast min to uintmax_t,但我不喜欢这个解决方案,因为它似乎不如我的首选解决方案可移植,甚至可能是未定义的行为,即使我怀疑是否可能在编译时发生。

3 个答案:

答案 0 :(得分:1)

分为4部分。每个min max至少为零。

如果它们共享相同的符号(0为正数),则2s补码整数可以将它们的差异表示为它们自己类型的一部分。

这会留下max<minmax肯定和min个否定案例。

如果我们假设uint_max_t足够大,则算术和转换为该类型的行为都符合数学模型2^n

所以unsigned(a)-unsigned(b)实际上是从无符号距离从b到达有符号整数。

C = A-B mod X
C = A-B + kX
B+C=A+kX

C为正且小于XX大于B-A,我们C必须为delta。

答案 1 :(得分:1)

我不确定为什么MSVC会发出警告,但是你正在做的一件事可能会导致不良行为,就是在算术运算和比较中混合有符号和无符号整数。

您可以阅读此内容,了解由此引起的问题示例:http://blog.regehr.org/archives/268

我会尝试重写你的功能:

constexpr unsigned bits_in_range(intmax_t min,intmax_t max)
{
    return bits_to_represent(
             static_cast<uintmax_t>(max) - static_cast<uintmax_t>(min));
}

这种方式对程序员更友好。当您对不匹配的整数类型进行算术运算时,编译器将不得不进行隐式转换以使它们匹配。这样,它不必这样做。即使max和min为负数,如果您确定max >= min,这仍然会给出明确定义的正确结果。

答案 2 :(得分:0)

感谢您的评论,即使他们没有解释微软的警告。 Clang编译干净,所以它可能是编译器中的一个错误。 由于在C ++中从有符号值转换为无符号值的性质,通过简单地转换两个值(再次假设min&lt; = max)来获得正确的答案:

constexpr unsigned bits_in_range(intmax_t min,intmax_t max)
{
    return bits_to_represent(static_cast<largest_uint>(max) -
                             static_cast<largest_uint>(min));
}

可以从标准草案的这一部分推断出代码的有效性(我查看了最新的草案,但我相信这里没有变化)。

  

4.7积分转换[conv.integral]

     

如果目标类型是无符号的,则结果值最小&gt;无符号整数与源一致   整数(模2n,其中n是用于表示的位数   无符号类型)。