使用二进制补码找到二进制数,C

时间:2017-09-10 20:54:18

标签: c binary

我想我可能会让这有点复杂。我们应该传入一个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;
}

6 个答案:

答案 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,但可以将其修改为签名或任何整数类型:(charint或{{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)valuevalue投射到unsigned long类型。如果该值在两种类型中都可表示,则该值保持不变;否则,使用模运算(ISO C11,6.3.1.3)。由于此时value为否定,因此广告代码值为valueULONG_MAX + 1UL

因此,要获得-value,我们会从ULONG_MAX + 1UL中减去投射值。因为ULONG_MAX1是常量,编译器可能希望首先将它们一起添加,然后抱怨它溢出了类型(它没有,因为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)

这是学生运动吗?在这种情况下,这个答案可能不是你想要的,因为它没有表明你理解这个练习,但它很可能是最有效的方法:

提供:

  • 不需要携带
  • gcc是正在使用的编译器

你可以使用__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字节的大查找等等。

如果是签名号码,只需使用相同的功能,但将签名号码转换为无符号号码。