表示范围所需位的编译时计算
Compile-time calculation of bits needed to represent a range
我需要在编译时计算表示一个范围所需的位数。
对于从 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 分钟到 uintmax_t,但我不喜欢这个解决方案,因为它看起来不如我首选的解决方案便携,甚至可能是未定义的行为,尽管我对此持怀疑态度可以在编译时发生。
分 4 个部分进行。每个最小最大值至少为零。
如果它们共享相同的符号(0 为正数),则 2 补整数可以将它们的差异表示为它们自己类型的一部分。
剩下 max<min
和 max
正面案例和 min
负面案例。
如果我们假设 uint_max_t
足够大,那么算术和到该类型的转换都会根据数学 mod 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
必须是增量。
我不确定 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
.
,这仍然会给出定义明确且正确的结果
感谢您的评论,尽管他们没有解释 Microsoft 警告。 Clang 编译干净,所以它可能是编译器中的错误。
由于 C++ 中从有符号值到无符号值的转换性质,正确的答案将通过简单地转换两个值(再次假设 min <= 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 Integral conversions [conv.integral]
If the destination type is unsigned, the resulting value is the least > unsigned integer congruent to the source
integer (modulo 2n where n is the number of bits used to represent the
unsigned type).
我需要在编译时计算表示一个范围所需的位数。 对于从 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 分钟到 uintmax_t,但我不喜欢这个解决方案,因为它看起来不如我首选的解决方案便携,甚至可能是未定义的行为,尽管我对此持怀疑态度可以在编译时发生。
分 4 个部分进行。每个最小最大值至少为零。
如果它们共享相同的符号(0 为正数),则 2 补整数可以将它们的差异表示为它们自己类型的一部分。
剩下 max<min
和 max
正面案例和 min
负面案例。
如果我们假设 uint_max_t
足够大,那么算术和到该类型的转换都会根据数学 mod 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
必须是增量。
我不确定 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
.
感谢您的评论,尽管他们没有解释 Microsoft 警告。 Clang 编译干净,所以它可能是编译器中的错误。 由于 C++ 中从有符号值到无符号值的转换性质,正确的答案将通过简单地转换两个值(再次假设 min <= 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 Integral conversions [conv.integral]
If the destination type is unsigned, the resulting value is the least > unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type).