问题描述
这是一个困扰我一段时间的问题。
请考虑将数字0b10101
(以10为底的21)除以二进制数字0b10
(以10为底的2)。由于被二除是右移一,因此,商(截断的)显然是0b1010
!
我正在尝试在C
中实现二进制除法
但是最近,我试图用笔和纸做这件事,并一头雾水地想停下来!
我的算法很简单,因为我通过适当的左移将除数的最高有效位与除数对齐,然后进行比较以得到商的位,然后进行减法以获得下一个迭代的下一个除数。
我们继续进行这一欧几里得分部,直到获得股息
但是, 考虑:
10 ) 10101 ( 101
-10000
------------
101
-1000
-------------
101
-100
-------------
1 // Why don't you stop here ?
// Since at this step the dividend is less than divisor?
我知道由于我的算法错误而获得的上述商是错误的!商应为0b1010
!
- 为什么我们将商左移一?
- 为什么我们要本质,等待对齐的位
除数
0b10000
等于原始除数的位数个位数0b10
,尽管已经成功计算了余数?
- 为什么我们要本质,等待对齐的位
除数
解决方法
与小学完全一样...
-------------
10 ) 10101
10变成1多少次?零
0
-------------
10 ) 10101
进10个? 1次
01
-------------
10 ) 10101
10
----
01
多少次进入01?零次
010
-------------
10 ) 10101
10
----
010
多少次变成10次?一次
0101
-------------
10 ) 10101
-10
----
010
-10
------
01
多少次进入01?零次
01010
-------------
10 ) 10101
-10
----
010
-10
------
01
答案:1010余数1
最大的区别是在小学时,您确实需要记住一个乘法表,对于二进制来说,它是零次或一次,并且您本质上可以将匹配/大于模式设置为小于/小于基于位的模式,而不必进行乘法运算。
编辑
小学,十进制
-----------
10 ) 1234
0
-----------
10 ) 1234
12
01
-----------
10 ) 1234
12
-10
---
23
012x
-----------
10 ) 1234
12
-10
---
23
-20
------
3
您告诉我,在小学时,您被教导到那里停下来,而不是填写分子上方的所有位置?你是那样悬空的那一列吗?没空?为什么以2为基数或以7为基数或以97为基数?
我们被教导要至少完成小数点,然后再根据所学的年份而定 但至少在这种情况下,请完成“ ones”列,然后您可以停止 用于整数数学。
10次进入34次3
0123
-----------
10 ) 1234
12
-10
---
23
-20
------
34
-30
------
4
这个特殊的数学原理没有什么不同。
1234/10是123而不是12。
0b10101 / 0b10是0b1010而不是0b101
编辑2
通过编程容易实现
------------
abc ) defg...
累加器ra,分子rb,分母rc,结果rd
在我们的小学,我们一次要处理一个分子数字,累加器只需简单地在其右边的下一个数字处加上一个分母,然后再次用分母进行测试,就可以将分子左移一位在那个时间点为累加器得到一个高于它的数字。因此,我们从一个空的累加器开始,每次迭代将ms位从分子中拉出,直到我们用尽了所有的数字/位,而不是全部。因此,第一个循环是用d位,第二个是用e位,第三个是用f位,依此类推,直到我们在整数分子中没有数字为止。
ra = 0;
rd = 0
For number of bits in the numerator:
ra<<=1; //make room for next bit from numerator
ra|=(rb>>(nbits-1))&1;
rb<<=1; //prep for next loop
rd<<=1; //make room for next result bit,fill with zero
if(ra>=rc) // <-base 2 optimization either it is a 0 or 1
{
rd|=1; //result for this bit is a 1 not zero
ra-=rc; //subtract one times denominator from accumulator
}
沿着这些思路,我已经验证了执行此操作的C代码。理解分子和分母以及其他变量的大小是有限的,您想使用32位变量,但是要使分子/分母的大小小得多,如10101和10。
与小学数学1234/10相同
从左移0的累加器开始,取第一个数字
1 / 10 = 0 remainder 1 (10^3 column 1)
其余为累加器,因此将下一位数字2左移1并插入2,使之成为12
12 / 10 = 1 remainder 2 (10^2 column 2)
23 / 10 = 2 remainder 3 (10^1 column 3)
34 / 10 = 3 remainder 4 (19^0 column 4)
所以现在我们覆盖了从10 ^ n到10 ^ 0的所有有效数字 千列到一列,即整数结果。
因此,您可以通过编程方式移位累加器,将累加器拉到下一位,将累加器与分母进行比较,确定结果的基数,减去累加器,重复进行,直到完成“ ones”列,或者继续操作即可。超出小数点/二进制点。
编辑3(固定)
如果您要重复进行减法操作
10101 - 10 = 10001 (1)
10011 - 10 = 10001 (2)
10001 - 10 = 1111 (3)
1111 - 10 = 1101 (4)
1101 - 10 = 1011 (5)
1011 - 10 = 1001 (6)
1001 - 10 = 111 (7)
111 - 10 = 101 (8)
101 - 10 = 11 (9)
11 - 10 = 1 (10)
是的,令人困惑的股息可能需要为零或更少。我进行了长期分割,该分割始终有效。
编辑4
#include <stdio.h>
unsigned int fun ( unsigned int a,unsigned int b )
{
unsigned int c;
c = 0;
while(a>b)
{
a-=b;
c++;
}
return(c);
}
int main ( void )
{
printf("%u\n",fun(21,2));
return(0);
}
gcc so.c -o so
./so
10
编辑5
#include <stdio.h>
unsigned int fun ( unsigned int a,unsigned int b )
{
unsigned int c;
c = 0;
while(a>b)
{
a-=b;
c++;
printf("%u %u %u \n",a,b,c);
}
return(c);
}
int main ( void )
{
printf("%u\n",2));
return(0);
}
19 2 1
17 2 2
15 2 3
13 2 4
11 2 5
9 2 6
7 2 7
5 2 8
3 2 9
1 2 10
10
#include <stdio.h>
unsigned int fun ( unsigned int a,unsigned int b )
{
unsigned int c;
c = 0;
while(a>b)
{
printf("%u %u %u \n",c);
a-=b;
c++;
}
return(c);
}
int main ( void )
{
printf("%u\n",2));
return(0);
}
21 2 0
19 2 1
17 2 2
15 2 3
13 2 4
11 2 5
9 2 6
7 2 7
5 2 8
3 2 9
10
#include <stdio.h>
unsigned int fun ( unsigned int a,unsigned int b )
{
unsigned int c;
c = 0;
while(a>b)
{
printf("%u %u ",b);
a-=b;
c++;
printf("%u \n",2));
return(0);
}
21 2 1
19 2 2
17 2 3
15 2 4
13 2 5
11 2 6
9 2 7
7 2 8
5 2 9
3 2 10
10
这就是问题所在,已修复编辑3。
10101 - 10 = 10001 (1)
10011 - 10 = 10001 (2)
10001 - 10 = 1111 (3)
1111 - 10 = 1101 (4)
1101 - 10 = 1011 (5)
1011 - 10 = 1001 (6)
1001 - 10 = 111 (7)
111 - 10 = 101 (8)
101 - 10 = 11 (9)
11 - 10 = 1 (10)
编辑6
#include <stdio.h>
unsigned int fun1 ( unsigned int a,unsigned int b )
{
unsigned int c;
c = 0;
while(a>b)
{
a-=b;
c++;
}
return(c);
}
unsigned int fun2 ( unsigned int a,unsigned int b )
{
unsigned int res;
unsigned int acc;
unsigned int rb;
res=0;
acc=0;
for(rb=0x80000000;rb;rb>>=1)
{
acc<<=1;
if(rb&a) acc|=1;
if(acc>=b)
{
acc-=b;
res|=rb;
}
}
return(res);
}
int main ( void )
{
printf("%u\n",fun1(21,2));
printf("%u\n",fun1(1234,10));
printf("%u\n",fun2(21,fun2(1234,10));
return(0);
}
10
123
10
123
很明显,您对无法一般地放入任何两个值并无法正常工作的事物的大小有所限制。
,我的固定解决方案:
uint32_t divide(uint32_t y,uint32_t x,uint32_t *q)
{
(*q) = 0;
if (y < x)
{
*q = 0;
return y;
}
uint32_t tq = 0;
uint32_t td = 0;
int shift = 0;
while (y >= x)
{
shift = bitlen(y) - bitlen(x);
tq = (1 << shift);
td = (x << shift);
while (y < td)
{
tq >>= 1;
td >>= 1;
}
y -= (td);
(*q) += tq;
}
return y;
}