按位乘法并在Java中添加

时间:2011-02-04 06:25:07

标签: java bit-manipulation bitwise-operators multiplication addition

我有同时进行乘法和加法的方法,但我无法理解它们。它们都来自外部网站而不是我自己的网站:

public static void bitwiseMultiply(int n1, int n2) {
    int a = n1, b = n2, result=0;
    while (b != 0) // Iterate the loop till b==0
    {
        if ((b & 01) != 0) // Logical ANDing of the value of b with 01
        {
            result = result + a; // Update the result with the new value of a.
        }
        a <<= 1;              // Left shifting the value contained in 'a' by 1.
        b >>= 1;             // Right shifting the value contained in 'b' by 1.
    }
    System.out.println(result);
}

public static void bitwiseAdd(int n1, int n2) {
    int x = n1, y = n2;
    int xor, and, temp;
    and = x & y;
    xor = x ^ y;

    while (and != 0) {
        and <<= 1;
        temp = xor ^ and;
        and &= xor;
        xor = temp;
    }
    System.out.println(xor);
}

我尝试了一步一步的调试,但它确实对我没有多大意义,尽管它有效。

我可能正在寻找的是试着理解它是如何工作的(也许是数学基础?)。

编辑:这不是家庭作业,我只是想学习Java中的按位操作。

4 个答案:

答案 0 :(得分:24)

让我们从查看乘法代码开始。这个想法实际上非常聪明。假设你有二进制写的n 1 和n 2 。然后你可以把n1想象为2的幂之和:n2 = c 30 2 30 + c 29 2 29 < / sup> + ... + c 1 2 1 + c 0 2 0 ,其中每个c < sub> i 是0或1.然后您可以将产品n 1 n 2 视为

n 1 n 2 =

n 1 (c 30 2 30 + c 29 2 29 + ... + c 1 2 1 + c 0 2 0 )=

n 1 c 30 2 30 + n 1 c 29 2 29 + ... + n 1 c 1 2 1 + n 1 c 0 2 0

这有点密集,但是这个想法是两个数字的乘积由第一个数字乘以构成第二个数字的2的幂,乘以第二个数字的二进制数字的值。

现在的问题是我们是否可以在不进行任何实际乘法的情况下计算此总和的项。为此,我们需要能够读取n 2 的二进制数字。幸运的是,我们可以使用轮班来做到这一点。特别是,假设我们从n 2 开始,然后只看最后一位。那是c 0 。如果我们然后将值向下移动一个位置,则最后一位是c 0 等。更一般地,在将n 2 的值向下移位i个位置之后,最低位将是c i 。为了读取最后一位,我们可以按数字1对该值进行按位AND。这具有二进制表示,除了最后一位之外,其他地方都是零。由于任何n都为0 AND n = 0,因此清除所有最高位。此外,由于0 AND 1 = 0且1 AND 1 = 1,此操作会保留数字的最后一位。

好的 - 我们现在知道我们可以读取c i 的值;所以呢?好吧,好消息是我们也可以用类似的方式计算n 1 2 i 系列的值。特别地,考虑值n 1 &lt;&lt;&lt;&lt; 0,n 1 &lt;&lt;每次执行左移位时,它相当于乘以2的幂。这意味着我们现在拥有计算上述总和所需的所有组件。这是您的原始源代码,评论了正在发生的事情:

public static void bitwiseMultiply(int n1, int n2) {
    /* This value will hold n1 * 2^i for varying values of i.  It will
     * start off holding n1 * 2^0 = n1, and after each iteration will 
     * be updated to hold the next term in the sequence.
     */
    int a = n1;

    /* This value will be used to read the individual bits out of n2.
     * We'll use the shifting trick to read the bits and will maintain
     * the invariant that after i iterations, b is equal to n2 >> i.
     */
    int b = n2;

    /* This value will hold the sum of the terms so far. */
    int result = 0;

    /* Continuously loop over more and more bits of n2 until we've
     * consumed the last of them.  Since after i iterations of the
     * loop b = n2 >> i, this only reaches zero once we've used up
     * all the bits of the original value of n2.
     */
    while (b != 0)
    {
        /* Using the bitwise AND trick, determine whether the ith 
         * bit of b is a zero or one.  If it's a zero, then the
         * current term in our sum is zero and we don't do anything.
         * Otherwise, then we should add n1 * 2^i.
         */
        if ((b & 1) != 0)
        {
            /* Recall that a = n1 * 2^i at this point, so we're adding
             * in the next term in the sum.
             */
            result = result + a;
        }

        /* To maintain that a = n1 * 2^i after i iterations, scale it
         * by a factor of two by left shifting one position.
         */
        a <<= 1;

        /* To maintain that b = n2 >> i after i iterations, shift it
         * one spot over.
         */
        b >>>= 1;
    }

    System.out.println(result);
}

希望这有帮助!

答案 1 :(得分:5)

看起来你的问题不是java,只是用二进制数来计算。简单的开始: (所有数字二进制:)

0 + 0 = 0   # 0 xor 0 = 0
0 + 1 = 1   # 0 xor 1 = 1
1 + 0 = 1   # 1 xor 0 = 1
1 + 1 = 10  # 1 xor 1 = 0 ( read 1 + 1 = 10 as 1 + 1 = 0 and 1 carry)

好的......你看到你可以使用xor操作添加两个一位数字。使用a,您现在可以确定是否有“进位”位,这与使用笔和纸添加数字非常相似。 (到目前为止,你有一个叫做半加法器的东西)。当您添加接下来的两位时,您还需要将进位位添加到这两位数。考虑到这一点,您可以获得Full-Adder。您可以在维基百科上阅读有关半加器和全加器的概念: http://en.wikipedia.org/wiki/Adder_(electronics) 网上有更多的地方。 我希望这能给你一个开始。

通过乘法,它非常相似。只要记住你在小学里如何用笔和纸张成倍增加。多数民众赞成在这里发生的事情。只是它发生了二进制数而不是十进制数。

答案 2 :(得分:5)

bitwiseAdd方法的说明:

我知道这个问题有一段时间被问过了,但由于没有给出关于bitwiseAdd方法如何在这里工作的完整答案。

理解bitwiseAdd中封装逻辑的关键在于添加操作与 xor 按位操作之间的关系。该关系由以下等式定义(关于该等式的数值示例,请参见附录1):

x + y = 2 * (x&y)+(x^y)     (1.1)

或更简单:

x + y = 2 * and + xor       (1.2)

with
    and = x & y
    xor = x ^ y

您可能已经注意到此等式中熟悉的内容:以及 xor 变量与在bitwiseAdd开头定义的变量相同。还有一个乘以2,在bitwiseAdd中,在while循环的开始处完成。但我稍后会再回过头来看。

让我也快速注意一下&#39;&amp;&#39;在我们继续前进之前按位运算。此运算符基本上是&#34;捕获&#34;应用它的位序列的交集。例如,9&amp; 13 = 1001&amp; 1101 = 1001(= 9)。从该结果可以看出,只有两个位序列共有的那些位被复制到结果中。由此得出,当两个比特序列没有公共比特时,应用&#39;&amp;&#39;在他们身上产生 0 这对加法 - 按位关系有重要影响,很快就会很清楚

现在我们遇到的问题是方程式1.2使用&#39; +&#39;运算符而bitwiseAdd不会(它只使用&#39; ^&#39;,&#39;&amp;&#39;和&#39;&lt;&lt;&lt;&quot;&#39;)。那么我们如何制作&#39; +&#39;方程式1.2不知何故消失了?答案:强迫&#39; 表达式返回0.我们这样做的方法是使用递归

为了证明这一点,我将一次推算方程式1.2(这一步可能有点具有挑战性,但如果需要的话,附录2中有详细的逐步结果):

x + y = 2*(2*and & xor) + (2*and ^ xor)     (1.3)

或更简单:

x + y = 2 * and[1] + xor[1]     (1.4)

with
    and[1] = 2*and & xor,
    xor[1] = 2*and ^ xor,
    [1] meaning 'recursed one time'

这里有几个有趣的事情需要注意。首先你注意到递归的概念听起来如何接近循环的概念,就像在bitwiseAdd中找到的那样。当您考虑和[1] 以及 xor [1] 时,此连接变得更加明显:它们与的表达式相同和 xor 表达式在bitwiseAdd中定义 INSIDE while循环。我们还注意到出现了一种模式:方程式1.4看起来与方程式1.2完全相同!

因此,如果保持递归符号,进行进一步的递归是轻而易举的。在这里,我们再次推算方程式1.2:

x + y = 2 * and[2] + xor[2]
x + y = 2 * and[3] + xor[3]

现在应该强调“临时”的作用。在bitwiseAdd中找到的变量: temp 允许从一个递归级别传递到下一个递归级别。

我们还注意到在所有这些方程中乘以2。如前所述,这个乘法是在bitwiseAdd的while循环开始时使用和&lt;&lt; = 1 语句完成的。这个乘法在下一个递归阶段有一个结果,因为和[i]中的位不同于前一阶段和[i]中的位(如果你还记得我之前做过的关于&#39; &amp;&#39;运营商你可能会看到现在的情况。)

等式1.4的一般形式现在变为:

x + y = 2 * and[x] + xor[x]     (1.5)
with x the nth recursion

<强> FINALY:

那么这个递归业务什么时候完全结束?

答案:当公式1.5的和[x] 表达式中的两个位序列之间的交点返回 0 时,它结束。当while循环条件变为false时,在bitwiseAdd中等效。此时,等式1.5变为:

    x + y = xor[x]      (1.6)

这解释了为什么在bitwiseAdd中我们最后只返回 xor

我们完成了!一个非常聪明的代码,这个bitwiseAdd我必须说:)

我希望这有帮助

<强>附录

1)等式1.1的数字示例

公式1.1说:

    x + y = 2(x&y)+(x^y)        (1.1)

要验证此声明,可以举一个简单的例子,比如说将9和13加在一起。步骤如下所示(按位表示在括号中):

我们有

    x = 9 (1001)
    y = 13  (1101)

    x + y = 9 + 13 = 22
    x & y = 9 & 13 = 9 (1001 & 1101 = 1001)
    x ^ y = 9^13 = 4 (1001 ^ 1101 = 0100)

将其重新纳入等式1.1,我们发现:

    9 + 13 = 2 * 9 + 4 = 22 et voila!

2)演示第一个递归步骤

演示文稿中的第一个递归方程(方程式1.3)表示

如果

x + y = 2 * and + xor           (equation 1.2)

然后

x + y = 2*(2*and & xor) + (2*and ^ xor)     (equation 1.3)

为了得到这个结果,我们简单地采用上面等式1.2的 2 *和+ xor 部分,并应用等式1.1给出的加法/按位操作数关系来它。这表现如下:

如果

    x + y = 2(x&y) + (x^y)      (equation 1.1)

然后

     [2(x&y)] + (x^y)     =      2 ([2(x&y)] & (x^y)) + ([2(x&y)] ^ (x^y))
(left side of equation 1.1)  (after applying the addition/bitwise operands relationship)

使用公式1.2的以及 xor 变量的定义对此进行简化,得出公式1.3的结果:

[2(x&y)] + (x^y) = 2*(2*and & xor) + (2*and ^ xor)

with
    and = x&y
    xor = x^y

使用相同的简化得出公式1.4的结果:

2*(2*and & xor) + (2*and ^ xor) = 2*and[1] + xor[1]

with
    and[1] = 2*and & xor
    xor[1] = 2*and ^ xor
    [1] meaning 'recursed one time'

答案 3 :(得分:0)

这是乘法的另一种方法

/**
 * Multiplication of binary numbers without using '*' operator
 * uses bitwise Shifting/Anding
 *
 * @param n1
 * @param n2
 */
public static void multiply(int n1, int n2) {
    int temp, i = 0, result = 0;

    while (n2 != 0) {
        if ((n2 & 1) == 1) {
            temp = n1;

            // result += (temp>>=(1/i)); // To do it only using Right shift
            result += (temp<<=i); // Left shift (temp * 2^i)
        }
        n2 >>= 1;   // Right shift n2 by 1.
        i++;
    }

    System.out.println(result);
}