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

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

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

14条回答
  •  栀梦
    栀梦 (楼主)
    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 
    #include 
    #include 
    
    #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
    

提交回复
热议问题