此站点:https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html表示它是为无符号整数定义的。将它用于signed int会在某些情况下给出错误的结果吗?
答案 0 :(得分:5)
__builtin_popcount
是特定于gcc的扩展程序。它的作用类似于声明:
int __builtin_popcount (unsigned int x);
如果你有一个具有该声明的实际函数,和它的声明是可见的,那么你可以将任何数字类型的参数传递给它。由于声明是原型,因此您传递的任何参数都将隐式转换为参数类型unsigned int
。
从(已签名)int
到unsigned int
的转换已明确定义。如果转换的值在0
.. INT_MAX
范围内,则值不变。否则,它会包裹模块UINT_MAX+1
。例如,将-1
转换为unsigned int
会产生UINT_MAX
,如果unsigned int
为32位宽,则为2 32 -1。
所以问题是,gcc是否将__builtin_popcount
视为具有可见原型的函数?由于它是一种语言扩展,它不会 ,并且gcc手册并不完全清楚。它显示了它的原型,但是 并不意味着原型对您的代码可见。
使用gcc 4.8.2进行的实验表明它被视为具有可见原型的函数。 (您不能像普通函数那样将其地址存储在指针中,但这不应该成为问题)。这个计划:
#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;
}
在我的系统上生成此输出:
popcount(21845) = 8
popcount(21845) = 8
popcount(1185589760) = 11
这意味着x
的值将转换为unsigned int
,而不仅仅是重新解释。当我们明确地重新解释它的表示时,我们会得到不同的结果。
因此,除非gcc因某种原因(似乎不太可能)更改其内置函数的实现,否则将签名的int
传递给__builtin_popcount
应该按预期工作,将int
值转换为{ {1}}。假设有符号整数的2-s补码表示(这是一个相当安全的假设),从unsigned int
转换为int
不会改变表示,因此unsigned int
将给出您正确计算__builtin_popcount
表示中设置的位数,包括符号位。
当然,如果你不想依赖于此,你总是可以使用强制转换将值显式转换为int
。转换通常容易出错,并且通常通常更好地使用隐式转换,但在这种情况下,它可能是一种合理的方法。
说完这一切之后,如果你计算一个值的总体数,那么从无符号值开始几乎肯定更有意义。您传递给unsigned int
的签名int
值可能1>首先被定义为__builtin_popcount
。
最后,您写道unsigned int
是&#34;为无符号整数定义&#34;。实际上,它仅针对类型__builtin_popcount
定义,而不是针对无符号整数定义。有三种不同的内置函数:
unsigned int
您需要使用正确的数据来处理您正在使用的数据类型。在int __builtin_popcount (unsigned int x);
int __builtin_popcountl (unsigned long x);
int __builtin_popcountll (unsigned long long x);
对象上使用__builtin_popcount
可能会忽略值的上半部分,可能没有来自编译器的警告。
答案 1 :(得分:3)
为了补充其他答案,这里是一个自己动手,做什么的gcc做的例子。让我们编写一个简单的测试用例:
$scope.logOut = function(user) {
ref.unauth();
};
并使用int f(int i){
return __builtin_popcount(i);
}
进行编译。这会创建几个文件,从gcc -c test.c -fdump-tree-all
开始:
test.c.003t.original
因此,您可以看到在有符号整数上调用;; Function f (null)
;; enabled by -tree-original
{
return __builtin_popcount ((unsigned int) i);
}
时,gcc会将其转换为记录的参数类型__builtin_popcount
。
答案 2 :(得分:1)
是。您也可以传递signed int
- 假设负数表示为2的补码(在现代系统中最多)。
如果数字是正数,则它与unsigned int
一样好。如果您传递一个负数,但是说-1
,它会转换为非常大的类型unsigned int
,但它不会改变位模式 - 因此位数< / strong>即可。 signed
或unsigned
与位模式无关,必须the interpretation of bits-pattern when computing value。
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!
希望有所帮助。