int x, y; // x is a non-negative integer
p = 0;
while (x > 0)
{
if ( x % 2 == 1 )
p = p + y;
y = y*2;
x = x/2;
}
// p == a*b here
我知道这个循环使用代数找到'a'和'b'的乘积:
a * b =(1/2)a * 2b
但我不明白代码:
if ( x % 2 == 1 )
p = p + y;
我希望有人可以解释为什么'p'在x的奇数值上被赋予'p + y'。
答案 0 :(得分:1)
因为这些是整数,a / 2
将是一个整数。如果a
为奇数,则该整数已向下舍入,并且在循环的下一次迭代中缺少一半b
,即当前迭代中的一整b
循环(因为b
[y
]每次加倍。)
答案 1 :(得分:1)
如果x为奇数,x = x/2
将x设置为小于x / 2(因为整数除法将其向下舍入)。需要调整p
以允许这样做。
答案 2 :(得分:1)
将乘法视为重复加法,x * y将y加在一起x次。将2 * y加在一起x / 2次也是一样的。从概念上讲,如果x是奇数,它有点不清楚。例如,如果x = 5且y = 3,那么添加2.5倍是什么意思?代码注意到当x是奇数时,加y,然后y = y * 2和x = x / 2。当x为奇数时,这会丢弃.5部分。因此,在此示例中,您添加y一次,然后x变为2(而不是2.5),因为整数除法会丢弃该分数。
在每个循环结束时,您将看到原始x和y的乘积对于p,x和y的当前值等于p + x * y。循环迭代直到x为0,结果完全在p。
如果您创建表并在每次循环时更新它,这也有助于查看发生了什么。这些是每次迭代开始时的值:
x | y | p
----------
5 | 3 | 0
2 | 6 | 3
1 | 12 | 3
0 | 24 | 15
答案 3 :(得分:1)
while (x > 0) {
if (x % 2 == 1)
p = p + y;
y = y*2;
x = x/2;
}
想象x
= 4,y = 5
迭代:
x
是偶数,y
= 10,x
= 2(即 x
可以划分,y
应该是一倍 强>)x
是偶数,y
= 20,x
= 1 x
是奇数,p
= 20,y
= 40,x
= 0(即 x
不能是已分割,y
应添加到p
)x > 0
为false
,循环结束
p
= 4 *y
现在假设x
在开头是奇怪的,让我们说x
= 5,y
= 2:
x
是奇数,p
= 2,y
= 4,x
= 2
(5/2 = 2.5, x
的新值将向下舍入,y
应在加倍之前添加 )x
是偶数,y
= 8,x
= 1 x
是奇数,p
= 10,y
= 16,x
= 0
p
=y
+ 4 *y
首先y
是原因,在结果加倍之前将其添加到结果中1 * y
)在这种情况下等同于0.5 * (2 * y)
答案 4 :(得分:1)
这可以通过观察(例如)y * 10 = y * 8 + y * 2
。
这就像在学校里做纸上乘法一样。例如,要乘以14 x 21,我们一次乘以一个数字(并在需要的地方左移一个数字),所以我们加1x14 + 2 x 14(左移一位)。
14
x 21
----
14
280
在这里,我们做了几乎相同的事情,但是使用二进制而不是十进制。正确的移位与奇数的数字无关,而只是简单地找到数字中的哪些位被设置。
当我们向右移动一个操作数以查找是否设置了一个位时,我们还将另一个操作数向左移动,就像我们在十进制纸上进行算术运算时添加零来移位数字一样。
因此,以二进制方式查看内容时,我们最终得到的结果如下:
101101
x 11010
--------
1011010
+ 101101000
+ 1011010000
如果我们想要,而不是正确地移动操作数,我们可以将掩模向左移动而不是and
与1
重复,我们and
与{{} 1}},然后使用1
,然后使用2
,依此类推(事实上,它可能会更有意义)。然而,无论好坏,在汇编语言中(通常都会执行此类操作),通常更容易移动操作数并使用常量作为掩码,而不是将掩码加载到寄存器中并在需要时将其移位。
答案 5 :(得分:0)
您应该将x重写为2 * b + 1(假设x为奇数)。然后
x*y = (2*b+1)*y = (2*b)*y + y = b*(2*y) + y = (x/2)*(2*y) + y
其中(x / 2)表示整数除法。通过这种方式重写操作,您会看到x / 2,2y和+ y出现。