[英]Can I use a signed integer as an argument to __builtin_popcount()?
This site: https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html says that it is defined for unsigned integers. 该站点: https : //gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html表示它是为无符号整数定义的。 Will using it for signed int give wrong results in some cases or not?
将它用于signed int会在某些情况下给出错误的结果吗?
__builtin_popcount
is a gcc-specific extension. __builtin_popcount
是特定于gcc的扩展。 It acts like a function with the declaration: 它的作用类似于声明的函数:
int __builtin_popcount (unsigned int x);
If you had an actual function with that declaration, and its declaration were visible, then you could pass an argument of any numeric type to it. 如果你有一个具有该声明的实际函数, 并且它的声明是可见的,那么你可以将任何数字类型的参数传递给它。 Since the declaration is a prototype, any argument you pass would be implicitly converted to the parameter type,
unsigned int
. 由于声明是原型,因此您传递的任何参数都将隐式转换为参数类型
unsigned int
。
Conversion from (signed) int
to unsigned int
is well defined. 从(signed)
int
到unsigned int
是明确定义的。 If the value being converted is within the range 0
.. INT_MAX
, the value is unchanged. 如果转换的值在
0
.. INT_MAX
范围内,则该值不变。 Otherwise, it's wrapped module UINT_MAX+1
. 否则,它的包装模块
UINT_MAX+1
。 For example, converting -1
to unsigned int
yields UINT_MAX
, which is 2 32 -1 if unsigned int
is 32 bits wide. 例如,将
-1
转换为unsigned int
产生UINT_MAX
,如果unsigned int
为32位宽,则为2 32 -1。
So the question is, does gcc treat __builtin_popcount
as a function with a visible prototype? 所以问题是,gcc是否将
__builtin_popcount
视为具有可见原型的函数? Since it's a language extension, it doesn't have to, and the gcc manual isn't entirely clear. 因为它是语言扩展,所以它没有必要 ,并且gcc手册并不完全清楚。 It shows a prototype for it, but that doesn't necessarily mean that prototype is visible to your code.
它显示了它的原型,但这并不一定意味着您的代码可以看到原型。
An experiment with gcc 4.8.2 indicates that it is treated as a function with a visible prototype. 使用gcc 4.8.2的实验表明它被视为具有可见原型的函数。 (You can't store its address in a pointer as you could for an ordinary function, but that shouldn't be a problem).
(您不能像普通函数那样将其地址存储在指针中,但这不应该是一个问题)。 This program:
这个程序:
#include <stdio.h>
#include <string.h>
int main(void) {
unsigned int n = 21845; // 0x5555, popcount = 8
float x = 21845.0;
unsigned int x_rep;
memcpy(&x_rep, &x, sizeof x_rep);
if (sizeof x != sizeof x_rep) {
puts("WARNING: Sizes do not match");
}
printf("popcount(%u) = %d\n", n, __builtin_popcount(n));
printf("popcount(%g) = %d\n", x, __builtin_popcount(x));
printf("popcount(%u) = %d\n", x_rep, __builtin_popcount(x_rep));
return 0;
}
produces this output on my system: 在我的系统上生成此输出:
popcount(21845) = 8
popcount(21845) = 8
popcount(1185589760) = 11
Which means that the value of x
is converted to unsigned int
, not just reinterpreted. 这意味着
x
的值被转换为unsigned int
,而不仅仅是重新解释。 When we explicitly reinterpret its representation, we get different results. 当我们明确地重新解释它的表示时,我们会得到不同的结果。
So unless gcc changes its implementation of builtin functions for some reason (that seems unlikely), passing a signed int
to __builtin_popcount
should work as expected, converting the int
value to unsigned int
. 因此,除非gcc由于某种原因(似乎不太可能)更改其内置函数的实现,否则将signed
int
传递给__builtin_popcount
应该按预期工作,将int
值转换为unsigned int
。 And assuming a 2's-complement representation for signed integers (which is a reasonably safe assumption), converting from int
to unsigned int
does not change the representation, so __builtin_popcount
will give you a correct count of the bits that are set in the representation of the int
, including the sign bit. 并假设有符号整数的二进制补码表示(这是一个相当安全的假设),从
int
转换为unsigned int
不会改变表示,因此__builtin_popcount
将为您提供在表示中设置的位的正确计数。 int
,包括符号位。
Of course if you don't want to depend on this, you can always explicitly convert the value to unsigned int
using a cast. 当然,如果您不想依赖于此,则始终可以使用强制转换将值显式转换为
unsigned int
。 Casts are often error-prone, and it's usually better to use implicit conversions, but in this case it might be a reasonable approach. 转换通常容易出错,使用隐式转换通常更好,但在这种情况下,它可能是一种合理的方法。
Having said all this, if you're computing the population count of a value, it almost certainly makes more sense to start with an unsigned value. 说完这一切之后,如果你计算一个值的总体数,那么从无符号值开始几乎肯定更有意义。 It's likely that the signed
int
value you're passing to __builtin_popcount
should have been defined as an unsigned int
in the first place. 这很可能是签署
int
你传递价值__builtin_popcount
应该被定义为unsigned int
放在首位。
Finally, you wrote that __builtin_popcount
is "defined for unsigned integers". 最后,您写道
__builtin_popcount
是“为无符号整数定义的”。 Actually, it's defined only for type unsigned int
, not for unsigned integers in general. 实际上,它只定义为
unsigned int
类型,而不是一般的无符号整数。 There are three different builtin functions: 有三种不同的内置函数:
int __builtin_popcount (unsigned int x);
int __builtin_popcountl (unsigned long x);
int __builtin_popcountll (unsigned long long x);
You need to use the right one for the type of data you're working with. 您需要使用正确的数据来处理您正在使用的数据类型。 Using
__builtin_popcount
on an unsigned long long
object will likely ignore the upper half of the value, probably without a warning from the compiler. 在
unsigned long long
对象上使用__builtin_popcount
可能会忽略值的上半部分,可能没有来自编译器的警告。
To complement the other answers, here is a do-it-yourself, what-is-gcc-doing example. 为了补充其他答案,这里是一个自己动手,做什么的gcc做的例子。 Let us write a simple testcase:
让我们编写一个简单的测试用例:
int f(int i){
return __builtin_popcount(i);
}
and compile it with gcc -c test.c -fdump-tree-all
. 并使用
gcc -c test.c -fdump-tree-all
编译它。 This creates several files, starting with test.c.003t.original
: 这将创建几个文件,从
test.c.003t.original
开始:
;; Function f (null)
;; enabled by -tree-original
{
return __builtin_popcount ((unsigned int) i);
}
So you can see that when __builtin_popcount
is called on a signed integer, gcc casts it to the documented argument type of unsigned int
. 因此,您可以看到,当在有符号整数上调用
__builtin_popcount
时,gcc会将其转换为unsigned int
的文档参数类型。
Yes. 是。 You can pass
signed int
as well — assuming negative number is represented as 2's complement (which is most on the modern systems) . 您也可以传递
signed int
- 假设负数表示为2的补码(在现代系统中最多) 。
If the number is positive, then it is as good as unsigned int
. 如果数字是正数,则它与
unsigned int
一样好。 If you pass a negative number however, say -1
, it will convert into a very large number of type unsigned int
, but it will not change the bits-pattern — hence the number of bits . 但是如果传递一个负数,比如
-1
,它将转换为非常大量的unsigned int
类型, 但它不会改变位模式 - 因此也就是位数 。 signed
or unsigned
has nothing to do with bit-patterns, it has to do the interpretation of bits-pattern when computing value . signed
或unsigned
与位模式无关,它必须在计算值时对位模式进行解释 。
signed int i = -1; //i has N number of 1 bit
unsigned int j = -1; //j has N number of 1 bit as well.
//j becomes a very large number!
Hope that helps. 希望有所帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.