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

Time:09-30

I want to add a value (potentially larger than 255) to an array of uint8_t. Currently, my implementation does not allow to exceed 255 as the value is of type uint8_t. Ideally I would like to have this value of type int.

This code must be compilable with standard libraries and be exclusively in C99. I don't have access to uint32_t or uint_64t types either. How could I modify the following snippet to change value from uint8t to 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;
}

CodePudding user response:

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

" Ideally I would like to have this value of type int. " --> As the goal is "a value greater than 255", we can simplify and use unsigned.

Add the unsigned to the uint8_t indexed in the array. As overflow may occur, use wider than 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;
}

... or more like OP's code with no wider integer math used.

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
}

CodePudding user response:

You say you're not allowed to use uint32_t or uint64_t, but you are allowed to use int. If so, this is simple enough. Just use int to pass in your new value and to keep track of the partial, running sum.

I did it like this; it's quite similar to the code @chux posted. I also simplified the logic: you don't actually need to keep separate sum and remaining values, so I boiled it down to just one variable, which I'm calling carry, of type 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;
    }
}

During each trip through the loop, carry is either the value we're adding in, or the carry that's leftover from the previous "digit". We add the carry to the current digit, stash as much of it as will fit — the low-order 8 bits — back into the current digit, and keep what's leftover (that is, after using >>= to discard the low-order 8 bits, which are they ones we've just stored in dest[i]) in the carry variable for next time.

Why does this work? Well, it's the same thing you do when you add a column of numbers by hand, using the technique we all learned back in elementary school. Suppose you're adding

      6
     14
      7
     23
     13
      4
   ----

After you add up the one's column, you get 27, and you say, "okay, that's 7, carry the 2". (You do not say, "carry the 20".) Your partial sum was 27, and the "bottom half" of it (7, or 27 % 10) is the digit in the one's place of the final sum, and the "top half" (2, or 27 / 10) is the carry into the ten's column.

So if you're not familiar or not comfortable with those "bitwise" operators, you can perform the equivalent operations using % and /, except using factors of 256 instead of 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;
    }
}

Basically you're performing addition here in base 256.

Depending on your needs and constraints, there might be some value in using types int16_t, unsigned int, or uint16_t for value and carry, instead of plain int.

I tested this by calling

addArray(testArray,arrayLength,300);

and it printed

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 

which looks right. As a second test,

addArray(testArray,arrayLength,123456789);

prints

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

(More on these tests in the "addendum" below.)

As @chux points out in a comment, this code does have its limitations. If the value you tried to add in was close to the maximum an int can hold, the carry variable might overflow (that is, on the carry = dest[i] carry line). But as long as value is less than or equal to INT_MAX - 255 (and greater than or equal to zero!), you should be fine.

Of course, if the value you're trying to add in might be large, it might also be larger than a single int can hold, in which case you'd need to pass it in some other way, likely as a second array of uint8_t, just like dest — and then you'd find yourself writing a fully general multiprecision adder.


Addendum: To double-check these results, and to demonstrate what's actually going on, we can perform the same arithmetic in dc, the Unix/Linux arbitrary-precision calculator. I have determined that @Tifa's initial key value is 3987683987354747618711421180841033724. We'll use dc to print that number out in various bases, and as we add 15, 300, and 123456789 to it.

$ 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's base-256 rendition corresponds to the output of Tifa's printArray function, albeit in a more conventional left-to-right order.

  •  Tags:  
  • c c99
  • Related