我想我可能会让这有点复杂。我们应该传入一个long,并在数字的二进制表示中返回1的数字。对于否定,我们返回两个补码。我有积极的工作,但两个补充有点偏。任何有关这项工作的提示都将受到赞赏。
unsigned int binaryOnesCounter(long n) {
unsigned int oneCounter, negCounter;
int binaryStorage[63];
int index, negFlag;
oneCounter = negFlag = index = 0;
int i;
for (i = 0; i < 63; ++i)
{
binaryStorage[i] = 0;
}
if(n < 0) {
if (n == -1){
oneCounter = 63 + 1;
} else {
/* negate and add 1*/
negFlag = 1;
n = (n * -1) + 1;
}
}
while (n>=1) {
if (n%2 == 1) {
oneCounter++;
binaryStorage[index] = 1;
}
else if (n%2 == 0) {
binaryStorage[index] = 0;
}
n = n/2;
}
if (negFlag == 1 && n != 1) {
negCounter = 64;
oneCounter = negCounter - oneCounter;
}
return oneCounter;
}
答案 0 :(得分:3)
它过于复杂
int countones(long long i)
{
int nOnes = 0;
unsigned long long *ptr = &i;
while (*ptr)
{
nOnes += *ptr & 1;
*ptr >>= 1;
}
return nOnes;
}
PS -4有62个没有63 0b1111111111111111111111111111111111111111111111111111111111111100
这是一个更普遍的(几乎在任何对象中计数)
int getbit(void *obj, size_t bit);
size_t countBitsUniversal(void *obj, size_t osize)
{
size_t nOnes = 0, tmp = osize;
osize <<= 3;
if (osize && osize > tmp)
{
do
{
nOnes += getbit(obj, --osize);
} while (osize);
}
return nOnes;
}
int getbit(void *obj, size_t bit)
{
uint8_t *p = obj;
return !!(p[bit >> 3] & (1 << (bit & 7)));
}
__________
usage example
double x;
printf("%zu\n", countBitsUniversal(&x, sizeof(x)));
long long arr[100];
printf("%zu\n", countBitsUniversal(arr, sizeof(arr)));
答案 1 :(得分:1)
经典解决方案:
int countBits(unsigned long long x)
{
int count = 0;
while (x)
{
count++;
x = x & (x - 1);
}
return count;
}
虽然我声明上面的参数为unsigned long long
,但可以将其修改为签名或任何整数类型:(char
,int
或{{1} })。
答案 2 :(得分:1)
一种简单的方法是使用位移和位掩码操作,并将n
置于正确的表示中(即n
表示正值,或2-complement表示负值{{1} }} S)。
如果您使用的系统表示负值已经是2补充(并且大多数常见系统都这样做),那么您只需将n
视为无符号值,即n
。然后,unsigned long ul = n
将包含否定ul
的2补语表示。
如果您使用另一种形式表示负值的系统(例如一位补码和一位符号位),您可以自行制作两位补码:
n
答案 3 :(得分:1)
考虑实际代表two's complement的方式。
假设你有一个功能
int bits_set_in_unsigned_long(unsigned long value);
以二进制表示形式value
返回1的数量。
我们可以直接向该功能提供非负输入。对于负输入,我们计算它们的二进制补码(在unsigned long
中),并根据问题定义返回其中的一个数:
int bits_set_in_long(long value)
{
if (value >= 0)
return bits_set_in_unsigned_long( (unsigned long)value );
else
return bits_set_in_unsigned_long( ~(ULONG_MAX - (unsigned long)value + 1UL) + 1UL );
}
#include <limits.h>
需要ULONG_MAX
。
上面是表达式
~(ULONG_MAX - (unsigned long)value + 1UL) + 1UL
将value
(类型为long
)转换为unsigned long
二进制补码。让我们看看这是如何完成的。
对于二进制补码表示,我们首先需要value
的否定值,作为无符号长整数。但是,在许多体系结构-LONG_MIN == LONG_MIN
上,因为LONG_MAX
的幅度小于LONG_MIN
。但是,因为我们现在知道该值是负数,所以我们可以首先抛出该值,然后调整其负值。
(unsigned long)value
将value
投射到unsigned long
类型。如果该值在两种类型中都可表示,则该值保持不变;否则,使用模运算(ISO C11,6.3.1.3)。由于此时value
为否定,因此广告代码值为value
加ULONG_MAX + 1UL
。
因此,要获得-value
,我们会从ULONG_MAX + 1UL
中减去投射值。因为ULONG_MAX
和1
是常量,编译器可能希望首先将它们一起添加,然后抱怨它溢出了类型(它没有,因为unsigned long
类型使用模运算,我们对此非常满意)。所以,我把减法放在中间,只是为了安静一些C编译器可能发出的警告(因为一些C编译器可能认为这样的结构通常是无意的错误,即使它们是完全标准的C代码)。
换句话说,(ULONG_MAX - (unsigned long)value + 1UL)
为value
提供了unsigned long
的幅度(或绝对值),因为value
此时为负值。
要将其转换为二进制补码格式,我们需要反转其所有二进制数字 - C ~
运算符完成 - 最后添加一个。因此,我们到达
~(ULONG_MAX - (unsigned long)value + 1UL) + 1UL
现在,还有其他方法可以在C中表达完全相同的东西,但我选择了这个p不知道任何关节的东西有两个简单的原因:第一,它是最容易证明是正确的,第二,我使用的编译器(GCC)将其视为具有负整数的二进制补码表示的架构上的无操作(或至少x86和x86-64,这是我在GCC-5.4.0上测试的)。换句话说,没有理由尝试“优化”表达式,除非你实际上有一个带有非二进制补码表示的结构用于负整数;只是因为表达式已经在两个补码架构中简化为无。
答案 4 :(得分:1)
这是学生运动吗?在这种情况下,这个答案可能不是你想要的,因为它没有表明你理解这个练习,但它很可能是最有效的方法:
提供:
你可以使用__builtin_popcount(),类似这样的东西(如果你的系统中有一个&#39; unsigned int&#39;是4个字节,而且&#39; long&#39;是8个字节):
unsigned int binaryOnesCounter(long n) {
unsigned int nrOfOnes = __builtin_popcount((unsigned int)(n & 0xffffffff));
nrOfOnes += __builtin_popcount((unsigned int)(n >> 32));
return nrOfOnes;
}
在某些架构上,这是在硬件中完成的,gcc可能会从中受益。
请注意,上面的代码示例假设sizeof(unsigned int)为4而sizeof(long)为8
有关gcc __builtins here
的更多信息答案 5 :(得分:1)
一种可能比移动32次更有效的方法是使用查找表。使用半字节查找的示例:
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
uint32_t count_ones (uint32_t data)
{
static const uint8_t ONES [16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
uint32_t count = 0;
while(data)
{
count += ONES[data & 0xF];
data >>= 4;
}
return count;
}
int main()
{
uint32_t data = 0xAABBCCDD;
printf("%"PRIu32, count_ones(data));
}
这可以扩展为以字节为单位查找,具有256字节的大查找等等。
如果是签名号码,只需使用相同的功能,但将签名号码转换为无符号号码。