如何将大于 255 的值添加到宽整数(编码为 uint_8t 的数组)?

How to add a value greater than 255 to a wide integer (encoded as an array of uint_8t)?

我想向 uint8_t 的数组添加一个值(可能大于 255)。目前,我的实现不允许超过 255,因为 valueuint8_t 类型。理想情况下,我希望这个值的类型为 int.

此代码必须可使用标准库进行编译,并且只能在 C99 中使用。我也无法访问 uint32_tuint_64t 类型。 我如何修改以下代码片段以将 valueuint8t 更改为 int ?

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

void addArray(uint8_t *dest, size_t sizeDest, uint8_t value){
    uint8_t remaining = value;
    uint8_t sum;
    for (size_t i = 0; i < sizeDest; ++i){
        sum = dest[i] + remaining;
        if (dest[i] > sum){
            remaining -= UINT8_MAX - (dest[i] - sum);
            dest[i]=sum;
        }
        else{
            dest[i]=sum;
            return;
        }
    }
}

void printArray(uint8_t *t, size_t arrayLength){
    for (int i = 0; i < arrayLength; ++i)
    {
        printf("%d ", t[i]);
    }
    printf("\n");
}

int main() {
    printf("Max value of uint8_t is %d\n",UINT8_MAX);
    int arrayLength = 16;
    uint8_t key[] = {
        252,255,255,255,255,255,255,255,
        255,255,255,255,255,255,255,2
    };
    uint8_t *testArray = (uint8_t *) malloc(sizeof(uint8_t) * arrayLength);
    memcpy(testArray, key, sizeof(uint8_t)*arrayLength);
    printf("Before addition\n");
    printArray(testArray, arrayLength);
    addArray(testArray,arrayLength,15);
    printf("After addition\n");
    printArray(testArray, arrayLength);
    return 0;
}

How to add a value greater than 255 to an array of uint_8t?

“理想情况下,我想要这个 int 类型的值。” --> 由于目标是“大于 255 的值”,我们可以简化并使用 unsigned.

unsigned 添加到数组中索引的 uint8_t。由于可能会发生溢出,请使用比 unsigned math.

更宽的范围
// Return overflow amount
// void addArray(uint8_t *dest, size_t sizeDest, uint8_t value){
unsigned addArray(uint8_t *dest, size_t sizeDest, unsigned value){
  unsigned long long sum = value; // Use some type wider than unsigned
  for (size_t i = 0; i < sizeDest; ++i) {
    sum += dest[i];               
    dest[i] = (uint8_t) sum;      // Save 8 lower bits
    sum >>= 8;                    // Continue with the upper bits
  }
  return (unsigned) sum;
}

...或更像 OP 的代码,没有使用更宽的整数数学运算。

unsigned addArray(uint8_t *dest, size_t sizeDest, unsigned value){
    #define UINT_MAX_PLUS1_DIV256 ((UINT_MAX - UINT_MAX/2) >> 7)
    unsigned remaining = value;
    for (size_t i = 0; i < sizeDest; ++i) {
        unsigned sum = remaining + dest[i];
        dest[i] = sum;   // Save lower bits
        if (sum < remaining) {  // Overflow occurred in remaining + dest[i]
            remaining = sum >> 8;
            remaining += UINT_MAX_PLUS1_DIV256 // add back overflow bit / 256
        } else{
            remaining = sum >> 8; // Continue with the upper bits 
        } 
    }
    return remaining
}

你说你不能使用uint32_tuint64_t,但你可以使用int。如果是这样,这很简单。只需使用 int 传递您的新值 来跟踪部分,运行 总和。

我是这样做的;它与@chux 发布的代码非常相似。我还简化了逻辑:您实际上不需要保留单独的 sumremaining 值,所以我将其归结为一个变量,我称之为 carry,输入 int.

void addArray(uint8_t *dest, size_t sizeDest, int value){
    int carry = value;
    for (size_t i = 0; i < sizeDest; ++i){
        carry = dest[i] + carry;
        dest[i] = carry & 0xff;
        carry >>= 8;
    }
}

在每次循环中,carry 要么是我们要添加的值,要么是前一个“数字”剩余的进位。我们将进位添加到当前数字,尽可能多地存储它 - 低位 8 位 - 回到当前数字,并保留剩余的(即,在使用 >>= 丢弃低位之后-order 8 位,它们是我们刚刚存储在 carry 变量中的 dest[i]) 以备下次使用。

为什么这行得通?好吧,这与您手动添加一列数字时所做的相同,使用的是我们在小学时都学过的技术。假设您要添加

      6
     14
      7
     23
     13
   +  4
   ----

将 1 的列加起来后,您得到 27,然后您说:“好吧,那是 7,进 2”。 (你 而不是 说,“携带 20”。)你的部分和是 27,它的“下半部分”(7,或 27 % 10)是最后和的一个位置,“上半部分”(2,或 27 / 10)是进入十列的进位。

因此,如果您不熟悉或不习惯那些“按位”运算符,您可以使用 %/ 执行等效操作,除了使用 256 的因数而不是 10:

void addArray(uint8_t *dest, size_t sizeDest, int value){
    int carry = value;
    for (size_t i = 0; i < sizeDest; ++i){
        carry = dest[i] + carry;
        dest[i] = carry % 256;
        carry /= 256;
    }
}

基本上你在这里以 256 为基数执行加法。

根据您的需要和限制,对 valuecarry 使用类型 int16_tunsigned intuint16_t 可能会有一些价值, 而不是普通的 int.

我通过调用

测试了这个
addArray(testArray,arrayLength,300);

它打印了

Before addition
252 255 255 255 255 255 255 255 255 255 255 255 255 255 255   2 
After addition
 40   1   0   0   0   0   0   0   0   0   0   0   0   0   0   3 

看起来不错。作为第二个测试,

addArray(testArray,arrayLength,123456789);

打印

After addition
 17 205  91   7   0   0   0   0   0   0   0   0   0   0   0   3 

(在下面的“附录”中有更多关于这些测试的信息。)

正如@chux 在评论中指出的那样,这段代码确实有其局限性。如果您尝试添加的值接近 int 可以容纳的最大值,则 carry 变量可能会溢出(即,在 carry = dest[i] + carry 行)。但只要 value 小于或等于 INT_MAX - 255(并且大于或等于零!),你应该没问题。

当然,如果您尝试添加的值可能很大,它也可能大于单个 int 可以容纳的值,在这种情况下,您需要将它传递给一些换句话说,可能是 uint8_t 的第二个数组,就像 dest 一样——然后你会发现自己正在编写一个完全通用的多精度加法器。


附录:为了仔细检查这些结果,并演示实际发生的情况,我们可以在 dc,即 Unix/Linux 任意精度计算器中执行相同的算法。我已经确定@Tifa 的初始 key 值为 3987683987354747618711421180841033724。我们将使用 dc 以各种基数打印出该数字,并向其添加 15、300 和 123456789。

$ dc
3987683987354747618711421180841033724       # original key
p                                           # print it
3987683987354747618711421180841033724
16o p                                       # set output base to 16 and print
2FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC
256o p                                      # set output base to 256 and print
 002 255 255 255 255 255 255 255 255 255 255 255 255 255 255 252
15 + p                                      # add 15 and print (still base 256)
 003 000 000 000 000 000 000 000 000 000 000 000 000 000 000 011
3987683987354747618711421180841033724       # original key again
300 + p                                     # add 300 and print
 003 000 000 000 000 000 000 000 000 000 000 000 000 000 001 040
3987683987354747618711421180841033724       # original key again
123456789 + p                               # add and print
 003 000 000 000 000 000 000 000 000 000 000 000 007 091 205 017

dc 的 base-256 格式对应于 Tifa 的 printArray 函数的输出,尽管是更传统的从左到右的顺序。