How can I multiply and divide using only bit shifting and adding?

后端 未结 14 1623
清歌不尽
清歌不尽 2020-11-22 15:09

How can I multiply and divide using only bit shifting and adding?

相关标签:
14条回答
  • 2020-11-22 15:38

    Try this. https://gist.github.com/swguru/5219592

    import sys
    # implement divide operation without using built-in divide operator
    def divAndMod_slow(y,x, debug=0):
        r = 0
        while y >= x:
                r += 1
                y -= x
        return r,y 
    
    
    # implement divide operation without using built-in divide operator
    def divAndMod(y,x, debug=0):
    
        ## find the highest position of positive bit of the ratio
        pos = -1
        while y >= x:
                pos += 1
                x <<= 1
        x >>= 1
        if debug: print "y=%d, x=%d, pos=%d" % (y,x,pos)
    
        if pos == -1:
                return 0, y
    
        r = 0
        while pos >= 0:
                if y >= x:
                        r += (1 << pos)                        
                        y -= x                
                if debug: print "y=%d, x=%d, r=%d, pos=%d" % (y,x,r,pos)
    
                x >>= 1
                pos -= 1
    
        return r, y
    
    
    if __name__ =="__main__":
        if len(sys.argv) == 3:
            y = int(sys.argv[1])
            x = int(sys.argv[2])
         else:
                y = 313271356
                x = 7
    
    print "=== Slow Version ...."
    res = divAndMod_slow( y, x)
    print "%d = %d * %d + %d" % (y, x, res[0], res[1])
    
    print "=== Fast Version ...."
    res = divAndMod( y, x, debug=1)
    print "%d = %d * %d + %d" % (y, x, res[0], res[1])
    
    0 讨论(0)
  • 2020-11-22 15:44

    A procedure for dividing integers that uses shifts and adds can be derived in straightforward fashion from decimal longhand division as taught in elementary school. The selection of each quotient digit is simplified, as the digit is either 0 and 1: if the current remainder is greater than or equal to the divisor, the least significant bit of the partial quotient is 1.

    Just as with decimal longhand division, the digits of the dividend are considered from most significant to least significant, one digit at a time. This is easily accomplished by a left shift in binary division. Also, quotient bits are gathered by left shifting the current quotient bits by one position, then appending the new quotient bit.

    In a classical arrangement, these two left shifts are combined into left shifting of one register pair. The upper half holds the current remainder, the lower half initial holds the dividend. As the dividend bits are transferred to the remainder register by left shift, the unused least significant bits of the lower half are used to accumulate the quotient bits.

    Below is x86 assembly language and C implementations of this algorithm. This particular variant of a shift & add division is sometimes referred to as the "no-performing" variant, as the subtraction of the divisor from the current remainder is not performed unless the remainder is greater than or equal to the divisor. In C, there is no notion of the carry flag used by the assembly version in the register pair left shift. Instead, it is emulated, based on the observation that the result of an addition modulo 2n can be smaller that either addend only if there was a carry out.

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    
    #define USE_ASM 0
    
    #if USE_ASM
    uint32_t bitwise_division (uint32_t dividend, uint32_t divisor)
    {
        uint32_t quot;
        __asm {
            mov  eax, [dividend];// quot = dividend
            mov  ecx, [divisor]; // divisor
            mov  edx, 32;        // bits_left
            mov  ebx, 0;         // rem
        $div_loop:
            add  eax, eax;       // (rem:quot) << 1
            adc  ebx, ebx;       //  ...
            cmp  ebx, ecx;       // rem >= divisor ?
            jb  $quot_bit_is_0;  // if (rem < divisor)
        $quot_bit_is_1:          // 
            sub  ebx, ecx;       // rem = rem - divisor
            add  eax, 1;         // quot++
        $quot_bit_is_0:
            dec  edx;            // bits_left--
            jnz  $div_loop;      // while (bits_left)
            mov  [quot], eax;    // quot
        }            
        return quot;
    }
    #else
    uint32_t bitwise_division (uint32_t dividend, uint32_t divisor)
    {
        uint32_t quot, rem, t;
        int bits_left = CHAR_BIT * sizeof (uint32_t);
    
        quot = dividend;
        rem = 0;
        do {
                // (rem:quot) << 1
                t = quot;
                quot = quot + quot;
                rem = rem + rem + (quot < t);
    
                if (rem >= divisor) {
                    rem = rem - divisor;
                    quot = quot + 1;
                }
                bits_left--;
        } while (bits_left);
        return quot;
    }
    #endif
    
    0 讨论(0)
  • 2020-11-22 15:46

    The below method is the implementation of binary divide considering both numbers are positive. If subtraction is a concern we can implement that as well using binary operators.

    Code

    -(int)binaryDivide:(int)numerator with:(int)denominator
    {
        if (numerator == 0 || denominator == 1) {
            return numerator;
        }
    
        if (denominator == 0) {
    
            #ifdef DEBUG
                NSAssert(denominator==0, @"denominator should be greater then 0");
            #endif
            return INFINITY;
        }
    
        // if (numerator <0) {
        //     numerator = abs(numerator);
        // }
    
        int maxBitDenom = [self getMaxBit:denominator];
        int maxBitNumerator = [self getMaxBit:numerator];
        int msbNumber = [self getMSB:maxBitDenom ofNumber:numerator];
    
        int qoutient = 0;
    
        int subResult = 0;
    
        int remainingBits = maxBitNumerator-maxBitDenom;
    
        if (msbNumber >= denominator) {
            qoutient |=1;
            subResult = msbNumber - denominator;
        }
        else {
            subResult = msbNumber;
        }
    
        while (remainingBits > 0) {
            int msbBit = (numerator & (1 << (remainingBits-1)))>0?1:0;
            subResult = (subResult << 1) | msbBit;
            if(subResult >= denominator) {
                subResult = subResult - denominator;
                qoutient= (qoutient << 1) | 1;
            }
            else{
                qoutient = qoutient << 1;
            }
            remainingBits--;
    
        }
        return qoutient;
    }
    
    -(int)getMaxBit:(int)inputNumber
    {
        int maxBit = 0;
        BOOL isMaxBitSet = NO;
        for (int i=0; i<sizeof(inputNumber)*8; i++) {
            if (inputNumber & (1<<i)) {
                maxBit = i;
                isMaxBitSet=YES;
            }
        }
        if (isMaxBitSet) {
            maxBit+=1;
        }
        return maxBit;
    }
    
    
    -(int)getMSB:(int)bits ofNumber:(int)number
    {
        int numbeMaxBit = [self getMaxBit:number];
        return number >> (numbeMaxBit - bits);
    }
    

    For multiplication:

    -(int)multiplyNumber:(int)num1 withNumber:(int)num2
    {
        int mulResult = 0;
        int ithBit;
    
        BOOL isNegativeSign = (num1<0 && num2>0) || (num1>0 && num2<0);
        num1 = abs(num1);
        num2 = abs(num2);
    
    
        for (int i=0; i<sizeof(num2)*8; i++)
        {
            ithBit =  num2 & (1<<i);
            if (ithBit>0) {
                mulResult += (num1 << i);
            }
    
        }
    
        if (isNegativeSign) {
            mulResult =  ((~mulResult)+1);
        }
    
        return mulResult;
    }
    
    0 讨论(0)
  • 2020-11-22 15:49

    Take two numbers, lets say 9 and 10, write them as binary - 1001 and 1010.

    Start with a result, R, of 0.

    Take one of the numbers, 1010 in this case, we'll call it A, and shift it right by one bit, if you shift out a one, add the first number, we'll call it B, to R.

    Now shift B left by one bit and repeat until all bits have been shifted out of A.

    It's easier to see what's going on if you see it written out, this is the example:

          0
       0000      0
      10010      1
     000000      0
    1001000      1
     ------
    1011010
    
    0 讨论(0)
  • 2020-11-22 15:50

    To multiply in terms of adding and shifting you want to decompose one of the numbers by powers of two, like so:

    21 * 5 = 10101_2 * 101_2             (Initial step)
           = 10101_2 * (1 * 2^2  +  0 * 2^1  +  1 * 2^0)
           = 10101_2 * 2^2 + 10101_2 * 2^0 
           = 10101_2 << 2 + 10101_2 << 0 (Decomposed)
           = 10101_2 * 4 + 10101_2 * 1
           = 10101_2 * 5
           = 21 * 5                      (Same as initial expression)
    

    (_2 means base 2)

    As you can see, multiplication can be decomposed into adding and shifting and back again. This is also why multiplication takes longer than bit shifts or adding - it's O(n^2) rather than O(n) in the number of bits. Real computer systems (as opposed to theoretical computer systems) have a finite number of bits, so multiplication takes a constant multiple of time compared to addition and shifting. If I recall correctly, modern processors, if pipelined properly, can do multiplication just about as fast as addition, by messing with the utilization of the ALUs (arithmetic units) in the processor.

    0 讨论(0)
  • 2020-11-22 15:50
    1. A left shift by 1 position is analogous to multiplying by 2. A right shift is analogous to dividing by 2.
    2. You can add in a loop to multiply. By picking the loop variable and the addition variable correctly, you can bound performance. Once you've explored that, you should use Peasant Multiplication
    0 讨论(0)
提交回复
热议问题