0

I've created an algorithm for dividing a integer up to 255 bytes in size by an 8 bit integer and it works with the tests I've done. Does anyone have any comments for it or any improvement suggestions? Is there a better algorithm for this purpose? I don't want a bignum by bignum division algorithm, the second integer is an 8 bit integer.

Best solution so far (small endian):

typedef struct{
    u_int8_t * data;
    u_int8_t length;
}CBBigInt;
void CBBigIntEqualsDivisionByUInt8(CBBigInt * a,u_int8_t b,u_int8_t * ans){
    // base-256 long division.
    u_int16_t temp = 0;
    for (u_int8_t x = a->length-1;; x--) {
        temp <<= 8;
        temp |= a->data[x];
        ans[x] = temp / b;
        temp -= ans[x] * b;
        if (!x)
            break;
    }
    a->length -= ans[a->length-1]? 0 : 1; // If last byte is zero, adjust length.
    memmove(a->data, ans, a->length); // Done calculation. Move ans to "a".
}

Old solution with big endian:

It works by:

  1. If the divisor is a power of two, do a bit shift to the right.
  2. Else figure how much the divisor needs to be shifted left before it is larger than the dividend and make a 16 bit integer become the divisor as if it were shifted to the left. This will be used for subtraction.
  3. Set a bit corresponding to the shift amount on the answer. What was done is the amount the dividend can fit into the divisor up to a power of two was found.
  4. Take away the shifted byte from the dividend to create a remainder.
  5. Repeat from step 2 for the remainder, until the divisor is larger than the remainder. When this occurs the answer is found.

typedef struct{
    u_int8_t * data;
    u_int8_t length;
}CBBigInt;
u_int8_t CBPowerOf2Log2(u_int8_t a){
    switch (a) {
        case 1:
            return 0;
        case 2:
            return 1;
        case 4:
            return 2;
        case 8:
            return 3;
        case 16:
            return 4;
        case 32:
            return 5;
        case 64:
            return 6;
    }
    return 7;
}
u_int8_t CBFloorLog2(u_int8_t a){
    if (a < 16){
        if (a < 4) {
            if (a == 1){
                return 0;
            }
            return 1;
        }
        if (a < 8){
            return 2;
        }
        return 3;
    }
    if (a < 64){
        if (a < 32) {
            return 4;
        }
        return 5;
    }
    if (a < 128){
        return 6;
    }
    return 7;
}
void CBBigIntEqualsRightShiftByUInt8(CBBigInt * a,u_int8_t b){
    u_int8_t deadBytes = b / 8; // These bytes fall off the side.
    a->length -= deadBytes; // Reduce length of bignum by the removed bytes
    u_int8_t remainderShift = b % 8;
    if (!remainderShift) { // No more work
        return;
    }
    u_int16_t splitter;
    u_int8_t toRight = 0; // Bits taken from the left to the next byte.
    for (u_int8_t x = 0; x < a->length; x++) {
        splitter = a->data[x] << 8 - remainderShift; // Splits data in splitters between first and second byte.
        a->data[x] = splitter >> 8; // First byte in splitter is the new data.
        a->data[x] |= toRight; // Take the bits from the left
        toRight = splitter; // Second byte is the data going to the right from this byte.
    }
}
void CBBigIntEqualsDivisionByUInt8(CBBigInt * a,u_int8_t b,u_int8_t * ans){
    if (!(b & (b - 1))){
        // For powers of two, division can be done through bit shifts.
        CBBigIntEqualsRightShiftByUInt8(a,CBPowerOf2Log2(b));
        return;
    }
    // Determine how many times b will fit into a as a power of two and repeat for the remainders
    u_int8_t begin = 0; // Begining of CBBigInt in calculations
    bool continuing = true;
    u_int8_t leftMost;
    bool first = true;
    while (continuing){
        // How much does b have to be shifted by before it becomes larger than a? Complete the shift into a shiftedByte
        int16_t shiftAmount;
        u_int16_t shiftedByte;
        if (a->data[begin] > b){
            shiftAmount = CBFloorLog2(a->data[begin]/b);
            shiftedByte = b << 8 + shiftAmount;
        }else if (a->data[begin] < b){
            shiftAmount = -CBFloorLog2(b/a->data[begin]);
            shiftedByte = b << 8 + shiftAmount;
            // Shift right once again if "shiftedByte > (a->data[begin] << 8) + a->data[begin+1]" as the shifted divisor should be smaller
            if (shiftedByte > ((a->data[begin] << 8) + a->data[begin+1])){
                shiftedByte >>= 1;
                shiftAmount--; // Do not forget about changing "shiftAmount" for calculations
            }
        }else{
            shiftAmount = 0;
            shiftedByte = b << 8;
        }
        // Set bit on "ans"
        if (shiftAmount < 0){ // If "shiftAmount" is negative then the byte moves right.
            ans[begin+1] |= 1 << (8 + shiftAmount);
            if (first) leftMost = 1;
        }else{
            ans[begin] |= 1 << shiftAmount; // No movement to right byte, jsut shift bit into place.
            if (first) leftMost = 0;
        }
        first = false; // Do not set "leftMost" from here on
        // Take away the shifted byte to give the remainder
        u_int16_t sub = (a->data[begin] << 8) + a->data[begin+1] - shiftedByte;
        a->data[begin] = sub >> 8;
        if (begin != a->length - 1) 
            a->data[begin + 1] = sub; // Move second byte into next data byte if exists.
        // Move along "begin" to byte with more data
        for (u_int8_t x = begin;; x++){
            if (a->data[x]){
                if (x == a->length - 1)
                    // Last byte
                    if (a->data[x] < b){
                        // b can fit no more
                        continuing = false;
                        break;
                    }
                begin = x;
                break;
            }
            if (x == a->length - 1){
                continuing = false; // No more data
                break;
            }
        }
    }
    a->length -= leftMost; // If the first bit was onto the 2nd byte then the length is less one
    memmove(a->data, ans + leftMost, a->length); // Done calculation. Move ans to "a".
}

Thanks!

Matthew Mitchell
  • 5,293
  • 14
  • 70
  • 122

1 Answers1

2

I'd describe this as "base 2 long division". A better alternative is "base 256 long division".

Here's an (untested and probably buggy) example:

typedef struct{
    u_int8_t * data;
    u_int8_t length;
} CBBigInt;


u_int8_t CBBigIntEqualsDivisionByUInt8(CBBigInt * a, u_int8_t b, u_int8_t * ans) {
    int i;
    unsigned int temp = 0;

    i = a.length;
    while(i > 0) {
       i--;
       temp <<= 8;
       temp |= a.data[i];
       ans.data[i] = temp / b;
       temp -= ans.data[i] * b;
    }
    return temp;   // Return remainder
}
Brendan
  • 35,656
  • 2
  • 39
  • 66
  • Is your CBBigInt data in big-endian or little-endian format? I assumed little-endian.. – Brendan May 10 '12 at 04:34
  • Thats it! I'm going to convert my bignums to little-endian (I think that is best on balance) and try again. – Matthew Mitchell May 10 '12 at 20:07
  • It still fails with little endian but not for every byte. It gets some bytes correct... – Matthew Mitchell May 10 '12 at 21:05
  • Oh no, it works. Funnily my testing code had a small issue. Thanks a bunch. I'll accept this as the answer. Any more suggestions welcome! – Matthew Mitchell May 10 '12 at 21:14
  • Good to see that works! The next step would be switching to "base 65536 long division" or possibly "base 4294967296 long division" to reduce the number of times you need to loop (and also increase the size of divisor it can handle). Beyond that you start getting into micro-optimisations (maybe assembly). – Brendan May 11 '12 at 01:36
  • The algorithm can always be optimised in the future. This is more than adequate for my needs. Thanks. – Matthew Mitchell May 14 '12 at 14:11