以二进制(位)减去有符号整数数据的最有效方法是什么?

时间:2012-02-28 04:22:09

标签: performance binary bits subtraction

我在PC上使用C语言,试图利用尽可能少的C ++,使用以无符号字符格式存储的二进制数据,尽管其他格式肯定是可行的,如果值得的话。目标是以二进制形式减去两个有符号整数值(可以是整数,有符号整数,长整数,有符号长整数,有符号短路等),而无需转换为其他数据格式。原始数据只是预先打包为unsigned char,用户基本上知道哪些有符号整数格式应该用于读取(即我们知道一次读取多少字节)。即使数据存储为unsigned char数组,数据也应作为二进制补码整数读取。

我们经常在学校教授的一种常见方式是添加否定词。反过来,否定通常被教导为执行翻转位并添加1(0x1),导致两次添加(可能是坏事?);或者,正如其他帖子指出的那样,从MSB开始翻转位超过第一个零点。我想知道是否有一种更有效的方法,可能不容易被描述为纸笔操作,但是因为数据以位格式存储的方式起作用。以下是我编写的一些原型,这可能不是最有效的方法,但总结了我目前基于教科书方法的进展。

如果我必须手动扩展它们以平衡它们的长度,则通过引用传递加数。任何和所有反馈将不胜感激!在此先感谢您的考虑。

void SubtractByte(unsigned char* & a, unsigned int & aBytes,
              unsigned char* & b, unsigned int & bBytes,
              unsigned char* & diff, unsigned int & nBytes)
{
    NegateByte(b, bBytes);

    // a - b == a + (-b)
    AddByte(a, aBytes, b, bBytes, diff, nBytes);

    // Restore b to its original state so input remains intact
    NegateByte(b, bBytes);
}

void AddByte(unsigned char* & a, unsigned int & aBytes,
             unsigned char* & b, unsigned int & bBytes,
             unsigned char* & sum, unsigned int & nBytes)
{
    // Ensure that both of our addends have the same length in memory:
    BalanceNumBytes(a, aBytes, b, bBytes, nBytes);
    bool aSign = !((a[aBytes-1] >> 7) & 0x1);
    bool bSign = !((b[bBytes-1] >> 7) & 0x1);


    // Add bit-by-bit to keep track of carry bit:
    unsigned int nBits = nBytes * BITS_PER_BYTE;
    unsigned char carry = 0x0;
    unsigned char result = 0x0;
    unsigned char a1, b1;
    // init sum
    for (unsigned int j = 0; j < nBytes; ++j) {
        for (unsigned int i = 0; i < BITS_PER_BYTE; ++i) {
            a1 = ((a[j] >> i) & 0x1);
            b1 = ((b[j] >> i) & 0x1);
            AddBit(&a1, &b1, &carry, &result);
            SetBit(sum, j, i, result==0x1);
        }
    }

    // MSB and carry determine if we need to extend:
    if (((aSign && bSign) && (carry != 0x0 || result != 0x0)) ||
        ((!aSign && !bSign) && (result == 0x0))) {
        ++nBytes;
        sum = (unsigned char*)realloc(sum, nBytes);
        sum[nBytes-1] = (carry == 0x0 ? 0x0 : 0xFF); //init
    }
}


void FlipByte (unsigned char* n, unsigned int nBytes)
{
    for (unsigned int i = 0; i < nBytes; ++i) {
        n[i] = ~n[i];
    }
}

void NegateByte (unsigned char* n, unsigned int nBytes)
{
    // Flip each bit:
    FlipByte(n, nBytes);
    unsigned char* one = (unsigned char*)malloc(nBytes);
    unsigned char* orig = (unsigned char*)malloc(nBytes);
    one[0] = 0x1;
    orig[0] = n[0];
    for (unsigned int i = 1; i < nBytes; ++i) {
        one[i] = 0x0;
        orig[i] = n[i];
    }
    // Add binary representation of 1
    AddByte(orig, nBytes, one, nBytes, n, nBytes);
    free(one);
    free(orig);
}

void AddBit(unsigned char* a, unsigned char* b, unsigned char* c,
unsigned char* result) {
     *result = ((*a + *b + *c) & 0x1);
     *c = (((*a + *b + *c) >> 1) & 0x1);
}

void SetBit(unsigned char* bytes, unsigned int byte, unsigned int bit,
bool val)
{
    // shift desired bit into LSB position, and AND with 00000001
    if (val) {
        // OR with 00001000
        bytes[byte] |= (0x01 << bit);
    }
    else{ // (!val), meaning we want to set to 0
        // AND with 11110111
        bytes[byte] &= ~(0x01 << bit);
    }
}

void BalanceNumBytes (unsigned char* & a, unsigned int & aBytes,
                      unsigned char* & b, unsigned int & bBytes,
                      unsigned int & nBytes)
{
    if (aBytes > bBytes) {
        nBytes = aBytes;
        b = (unsigned char*)realloc(b, nBytes);
        bBytes = nBytes;
        b[nBytes-1] = ((b[0] >> 7) & 0x1) ? 0xFF : 0x00;
    } else if (bBytes > aBytes) {
        nBytes = bBytes;
        a = (unsigned char*)realloc(a, nBytes);
        aBytes = nBytes;
        a[nBytes-1] = ((a[0] >> 7) & 0x1) ? 0xFF : 0x00;
    } else {
        nBytes = aBytes;
    }
}

1 个答案:

答案 0 :(得分:1)

首先要注意的是,有符号与无符号对于二进制补码中生成的位模式并不重要。所有这些变化都是对结果的解释。

要注意的第二件事是,如果在使用无符号算术时结果小于任一输入,则会进行加法。

void AddByte(unsigned char* & a, unsigned int & aBytes,
             unsigned char* & b, unsigned int & bBytes,
             unsigned char* & sum, unsigned int & nBytes)
{
    // Ensure that both of our addends have the same length in memory:
    BalanceNumBytes(a, aBytes, b, bBytes, nBytes);

    unsigned char carry = 0;
    for (int j = 0; j < nbytes; ++j) { // need to reverse the loop for big-endian
        result[j] = a[j] + b[j];
        unsigned char newcarry = (result[j] < a[j] || (unsigned char)(result[j]+carry) < a[j]);
        result[j] += carry;
        carry = newcarry;
    }
}