如何仅使用位移位和加法进行乘法和除法?
如何仅使用位移位和加法进行乘法和除法?
How can I multiply and divide using only bit shifting and adding?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如何仅使用位移位和加法进行乘法和除法?
How can I multiply and divide using only bit shifting and adding?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(15)
要以加法和移位的方式进行乘法,您需要将其中一个数字分解为 2 的幂,如下所示:(
_2
表示基数为 2)如您所见,乘法可以分解为加法和移位然后再回来。这也是为什么乘法比移位或加法需要更长的时间 - 它的位数是 O(n^2) 而不是 O(n)。真实的计算机系统(与理论计算机系统相反)的位数是有限的,因此与加法和移位相比,乘法需要恒定倍数的时间。如果我没记错的话,现代处理器如果流水线处理得当,通过扰乱处理器中 ALU(算术单元)的利用率,可以像加法一样快地执行乘法。
To multiply in terms of adding and shifting you want to decompose one of the numbers by powers of two, like so:
(
_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.
安德鲁·图卢兹的答案可以扩展到除法。
Henry S. Warren 所著的《Hacker's Delight》(ISBN 9780201914658)一书中详细讨论了整数常量除法。
实现除法的第一个想法是在基数 2 中写出分母的倒数。
例如,
1/3 = (base-2) 0.0101 0101 0101 0101 0101 0101 0101 0101 .....
所以,
a/3 = (a>>2) + (a>>4) + (a>>6) + ... + (a>>30)
用于 32 位算术。
通过以明显的方式组合这些项,我们可以减少运算次数:
b = (a >> 2) + (a >> 4)
b += (b >> 4)
b += (b >> 8)
b += (b >> 16)
还有更多精彩计算除法和余数的方法。
EDIT1:
如果OP意味着任意数字的乘法和除法,而不是除以常数,那么这个线程可能有用:https ://stackoverflow.com/a/12699549/1182653
编辑2:
除以整数常量的最快方法之一是利用模算术和蒙哥马利约简:将整数除以 3 的最快方法是什么?
The answer by Andrew Toulouse can be extended to division.
The division by integer constants is considered in details in the book "Hacker's Delight" by Henry S. Warren (ISBN 9780201914658).
The first idea for implementing division is to write the inverse value of the denominator in base two.
E.g.,
1/3 = (base-2) 0.0101 0101 0101 0101 0101 0101 0101 0101 .....
So,
a/3 = (a >> 2) + (a >> 4) + (a >> 6) + ... + (a >> 30)
for 32-bit arithmetics.
By combining the terms in an obvious manner we can reduce the number of operations:
b = (a >> 2) + (a >> 4)
b += (b >> 4)
b += (b >> 8)
b += (b >> 16)
There are more exciting ways to calculate division and remainders.
EDIT1:
If the OP means multiplication and division of arbitrary numbers, not the division by a constant number, then this thread might be of use: https://stackoverflow.com/a/12699549/1182653
EDIT2:
One of the fastest ways to divide by integer constants is to exploit the modular arithmetics and Montgomery reduction: What's the fastest way to divide an integer by 3?
X * 2 = 左移 1 位
X / 2 = 右移 1 位
X * 3 = 左移 1 位然后添加 X
X * 2 = 1 bit shift left
X / 2 = 1 bit shift right
X * 3 = shift left 1 bit and then add X
<代码>x << k == x 乘以 2 的 k 次方
x>> k == x 除以 2 的 k 次方
您可以使用这些移位来执行任何乘法运算。例如:
x * 14 == x * 16 - x * 2 == (x << 4) - (x << 1)
x * 12 == x * 8 + x * 4 == (x << 3) + (x << 2)
要将数字除以 2 的非幂,我不知道有什么简单的方法,除非您想实现一些低级逻辑,使用其他二进制运算并使用某种形式的迭代。
x << k == x multiplied by 2 to the power of k
x >> k == x divided by 2 to the power of k
You can use these shifts to do any multiplication operation. For example:
x * 14 == x * 16 - x * 2 == (x << 4) - (x << 1)
x * 12 == x * 8 + x * 4 == (x << 3) + (x << 2)
To divide a number by a non-power of two, I'm not aware of any easy way, unless you want to implement some low-level logic, use other binary operations and use some form of iteration.
使用移位和加法的整数除法过程可以直接从小学中教授的十进制普通除法导出。每个商位的选择都得到了简化,因为该位要么是 0,要么是 1:如果当前余数大于或等于除数,则部分商的最低有效位为 1。
与十进制普通除法一样,股息的数字按从最高有效位到最低有效位的顺序考虑,一次一位数字。这可以通过二进制除法的左移轻松完成。此外,通过将当前商位左移一位,然后附加新的商位来收集商位。
在经典布置中,这两个左移被组合成一对寄存器的左移。上半部分保存当前余额,下半部分初始保存股息。当被除数位通过左移传送到余数寄存器时,下半部分未使用的最低有效位用于累加商位。
下面是该算法的 x86 汇编语言和 C 实现。这种特殊的转变和变体加除法有时被称为“非执行”变体,因为除非余数大于或等于除数,否则不会执行从当前余数中减去除数的操作(Otto Spaniol,“计算机算术:逻辑与设计”) ” Chichester:Wiley 1981,第 144 页)。在 C 语言中,寄存器对左移中没有汇编版本使用的进位标志的概念。相反,它是基于以下观察进行模拟的:仅当存在进位时,模 2n 的加法结果才能小于任一加数。
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 "non-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 (Otto Spaniol, "Computer Arithmetic: Logic and Design." Chichester: Wiley 1981, p. 144). 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.
我将 Python 代码翻译成 C。给出的示例有一个小缺陷。如果被除数值占用了全部32位,则移位会失败。我只是在内部使用 64 位变量来解决这个问题:
I translated the Python code to C. The example given had a minor flaw. If the dividend value that took up all the 32 bits, the shift would fail. I just used 64-bit variables internally to work around the problem:
取两个数字,比如 9 和 10,将它们写为二进制 - 1001 和 1010。
从结果 R 开始,为 0。
取其中一个数字,在本例中为 1010,我们将其称为 A,然后将其移位右移一位,如果移出一个 1,则添加第一个数字,我们将其称为 B,到 R。
现在将 B 左移一位并重复,直到所有位都移出 A。
更容易看到如果你看到它写出来,发生了什么事,这是例子:
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:
摘自此处。
这仅适用于除法:
Taken from here.
This is only for division:
这应该适用于乘法:
This should work for multiplication:
下面的方法是考虑到两个数字都是正数的二进制除法的实现。如果需要考虑减法,我们也可以使用二元运算符来实现。
代码:
乘法
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
For multiplication:
它基本上是与基数幂 2 相乘和除法 2
左移 = x * 2 ^ y
右移 = x / 2 ^ y
shl eax,2 = 2 * 2 ^ 2 = 8
shr eax,3 = 2 / 2 ^ 3 = 1/4
it is basically multiplying and dividing with the base power 2
shift left = x * 2 ^ y
shift right = x / 2 ^ y
shl eax,2 = 2 * 2 ^ 2 = 8
shr eax,3 = 2 / 2 ^ 3 = 1/4
对于任何对 16 位 x86 解决方案感兴趣的人,这里有一段由 JasonKnight 编写的代码1 (他还包括一个带符号的乘法片段,我还没有测试过)。但是,该代码在处理大输入时存在问题,其中“add bx,bx”部分会溢出。
固定版本:
或与 GCC 内联汇编相同:
For anyone interested in a 16-bit x86 solution, there is a piece of code by JasonKnight here1 (he also includes a signed multiply piece, which I haven't tested). However, that code has issues with large inputs, where the "add bx,bx" part would overflow.
The fixed version:
Or the same in GCC inline assembly:
您可以使用以下公式将某些*乘法/除法语句转换为位移位运算:
* 假设
y
是 2 的幂示例:
You can convert some* multiplication/division statements to bit shift operations using the formulae:
* Assuming
y
is a power of 2Examples:
因此,要获得变量除法所需的位,需要除法本身。
它是 MSB/DIVIDER - 是转移除法所需的权力位,它最终是一个先有鸡还是先有蛋的问题。
这有点奇怪,但是->
如果将系统中的所有其他数字相乘,实际上就是将其中一个数字除以其他数字。
然后,如果您在最低有效位处有任何零填充,则将整个系统移回原位。
也就是说,如果你不能忍受其中有条件的除法,如果你的方程只有不到 5 个数字,你的运行还不错,但我知道它还不是最好的解决方案......但我会保留思维。
So to get the bits required for the variable divide requires a divide itself.
its the msb/DIVIDER - is the bits of the powers required for the shifting of the divide, and it ends being a chicken and egg problem.
Its a bit strange but->
If you multiply all the other numbers of your system, you've essentially divided that one number compared to the rest.
then just shift the whole system back if you got any zero padding at the lsb's.
Thats if u cant stand the division method with the condition in it, if theres only less than 5 numbers to your equation your running its not too bad to do, but I know its not the best solution yet... but i'll keep thinking.