我可以使用有符号整数作为__builtin_popcount()的参数吗?

时间:2015-04-26 15:02:05

标签: c++ gcc g++ gnu

此站点:https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html表示它是为无符号整数定义的。将它用于signed int会在某些情况下给出错误的结果吗?

3 个答案:

答案 0 :(得分:5)

__builtin_popcount是特定于gcc的扩展程序。它的作用类似于声明:

int __builtin_popcount (unsigned int x);

如果你有一个具有该声明的实际函数,它的声明是可见的,那么你可以将任何数字类型的参数传递给它。由于声明是原型,因此您传递的任何参数都将隐式转换为参数类型unsigned int

从(已签名)intunsigned 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可能首先被定义为__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>即可。 signedunsigned与位模式无关,必须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!

希望有所帮助。