【问题标题】:What is the most efficient way to subtract signed integral data in binary (bits)?以二进制(位)减去有符号整数数据的最有效方法是什么?
【发布时间】:2012-03-17 14:15:21
【问题描述】:

我在 PC 上使用 C 语言工作,试图尽可能少地利用 C++,处理以 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;
    }
}

【问题讨论】:

    标签: performance binary bits subtraction


    【解决方案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;
        }
    }
    

    【讨论】:

    • 感谢您提醒我字节序!我总是忘记那个。并感谢您对检查携带的见解。我想我仍然需要检查 MSB,以防万一 MSB 变为 1 表示正相加,或 0 表示负相加时,我们需要扩展。
    • 否定有什么特别奇怪的地方吗?例如,有没有比分配 0x1 并将其添加到位翻转原始值更快、更清洁的方法?再次感谢您的帮助。
    • @Cindeselia 我认为您已经找到了唯一需要担心签名与未签名的情况 - 执行 MSB。对于无符号,如果在循环结束后设置进位,则会出现溢出。对于有符号,它有点复杂 - 如果两个输入的 MSB 的高位不同,则不可能溢出,否则如果输入和输出之间的高位不同,则会发生溢出。我认为您完全理解否定过程,尽管不需要 malloc。
    猜你喜欢
    • 2019-02-23
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 2019-08-20
    • 2020-10-31
    • 1970-01-01
    相关资源
    最近更新 更多