【问题标题】:Compile-time calculation of bits needed to represent a range表示范围所需的位的编译时计算
【发布时间】:2016-06-13 20:35:04
【问题描述】:

我需要在编译时计算表示范围所需的位数。 对于从 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 转换为 uintmax_t,但我不喜欢这个解决方案,因为它似乎不如我的首选解决方案可移植,甚至可能是未定义的行为,尽管我怀疑这可能在编译时发生。

【问题讨论】:

  • 你能显示调用bits_in_range的代码吗?如果min &gt; max 那么我会期待这个警告,所以也许这是一个简单的错误。
  • 抱歉 - 应该一直提到 min
  • 错误出现在哪一行?
  • 我在 static_cast(max) - min.

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


【解决方案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 &gt;= min,这仍然会给出定义明确且正确的结果。

【讨论】:

    【解决方案2】:

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

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

    剩下 max&lt;minmax 正面和 min 负面案例。

    如果我们假设 uint_max_t 足够大,那么算术和转换到该类型的所有行为都根据数学模型 2^n

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

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

    如果C 为正且小于X,并且X 大于B-A,则C 必须是增量。

    【讨论】:

    • 对不起 - 应该提到我的算法需要 min
    • @user 我描述了为什么它定义明确。
    • 这是正确的,你告诉我的——但只是没有说服我。 ;-) 但是你当然是对的,我应该知道在 C++ 中从有符号到无符号的转换是如何工作的。实际上 - 假设像我一样 - min
    • 您需要更好地将允许的输入值定义为bits_in_range。例如,如果您允许 min 为负值,那么有人可以将 -1 传递给 minINTMAX_MAX 传递给 max,现在您需要表示的范围将大于 intmax_t 可以表示的范围,因此减去这些值将换行,因此是 UD。
    • @bee 在最大正数和最小负数中,您将两者都转换为无符号数。这是定义的行为。您将它们减去无符号。这是定义的行为。然后,我上面的论点表明,结果是它们之间的数学距离为整数。我错过了什么吗?
    【解决方案3】:

    感谢您的 cmets,尽管他们没有解释 Microsoft 警告。 Clang 编译干净,所以它可能是编译器中的一个错误。 由于在 C++ 中从有符号值转换为无符号值的性质,正确的答案将通过简单地转换两个值来获得(再次假设 min

    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]

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

    【讨论】:

      猜你喜欢
      • 2020-04-01
      • 1970-01-01
      • 2023-04-05
      • 2018-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多