算术移位除以奇数编号 - 微程序控制

时间:2013-09-15 18:29:31

标签: assembly architecture bit-manipulation

我正在尝试编写以下指令:

AC< - AC / 3。

我知道我可以使用算术右移以便按2进行除法,但是如何仅使用微程序控制(微指令)中可能存在的指令,将除数除以奇数。

谢谢

3 个答案:

答案 0 :(得分:3)

除以3,首先除以2,然后乘以2/3。在二进制文件中,2/30.101010101...。如果您具有转移承载能力,您可以:

  1. Q< - 股息SHR 2(将股息除以2)
  2. 通过移位/加法循环将值Q乘以1010101010101010,得到32位Q值(顶部16位字和底部16位字)
  3. 测试低16位字的高位。如果是1,请将1添加到Q
  4. 的高16位字
  5. Dividend/3的商是Q
  6. 的前16位字 但是,这有点变化和增加。此外,由于四舍五入,您可能需要1010101010101011而不是1010101010101010。我在Ruby中快速建模了这个方法,它似乎适用于几个简单的情况。

    您也可以在二进制11上运行直接除法算法(通过进位,比较,减去......转换为Dividend。你需要一些备用寄存器。但它可用于任何除数,而不仅仅是3。那只是微编码div指令。

答案 1 :(得分:3)

另一个答案建议除以2,乘以2/3。

如果你可以乘以2/3,你可以很容易地乘以1/3(二进制中的1/3 = .0101010101 ..)并将除数除以2。

要乘以1/3,您可以右移红利两个位置(对应于乘以.01!)并添加到累加器。重复乘法(呃,右移两次,乘以.0001,.000001,...)并根据需要添加多次,以处理预期红利所具有的最大位数。关注“掉头”的股息比特;假设你有足够的备用位,你需要一个双精度移位器/累加器,或者你需要在开始之前将对应的比特数除以2的正功率,以避免精度损失。

除以其他常数可以通过乘以构成其倒数的位来实现。它不是那么整洁,但想法是一样的。你可以找出一个变量,它在除以常数后计算模数(余数)。这两者都是编译器生成的代码中常见的技巧。

答案 2 :(得分:3)

Upvote Ira和mbratch,我只是在扩展他们的答案,以了解它的工作原理和原因。

基本上是小学的东西......记住基数10倍:

  1234
*   11
=======
  1234  (1*1234)
+12340  (10*1234)
=======
 13574

二进制文件使这更容易,因为数字只能是1或0没有2,3,4等...

  1111
*   11
=========
  1111 (1*1111)
+11110 (10*1111)
====== 
101101 

所以,如果我有一些通用位xyz,则乘以5然后

   xyz
*  101
========
   xyz
+xyz00
=======

因为5 = 4 + 1 = 2 ^ 2 + 2 ^ 0 = 1 <&lt; 2 + 1&lt; 0然后一些变量n * 5 =(n <&lt; 2)+(n&lt;&lt;&lt; 0 )

1/3的二进制是什么?那么为什么不使用小学的长分?

      0.01010101
     -----------
   11)1.00000000
      1          bring down the 1
     -0          0 times 3 first digit is a 0
    ==== 
       10        bring down the 0
      -00        0 times 3, second digit is 0
      ====
       100       bring down the 0
      - 11       1 times 3, next digit is a 1
      ====
         10      result 1, bring down 0
        - 0      0 times 3, next digit is a 0
        === 
         100     result is 2, bring down the 0
        - 11     1 times 3, next digit is a 1
        ==== 
           10

并且模式开始重复0.01010101 ...

因此,如果乘以5意味着二进制101 * n =(n <&lt; 2)+(n <&lt; 0),因为非零位在2 ^ 0和2 ^ 2位置。然后,如果你像上面的5一样进行乘法,那么如果有小数位则无关紧要。 0.1是功率-1的2,功率-2的0.01是2,依此类推,因此二进制1.01倍N将是(n <0)+(n> 2)。

最后乘以1/3,近似为0.0101010101 ....这意味着

result = (n>>2) + (n>>4) + (n>>6) + ...

正如其他人所指出的那样,你可以在一个循环中做到这一点,这就像。

result = 0;
while(n)
{
   n>>=2;
   result+=n;
}

就像在基数10中一样,当你用一个因子为3的东西除以得到一个无限重复的数字时,你在基数2中有同样的问题,一个无限的重复数。就像基数10,有时你想要0.6666666为0.666666,取决于多少位,但不是0.333333,你可能想要围绕你的除数并在那里有额外的位0.0101011或类似的东西。

divthree.c

unsigned int divthree ( unsigned int x )
{
    unsigned int y;

    y=0;
    x<<=16;
    while(x)
    {
        x>>=2;
        y+=x;
    }

// y + = 0x8000; //围捕?         Y'GT;&GT; = 16;         返回(Y);     }

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern unsigned int divthree ( unsigned int );
unsigned int next_prand ( unsigned int x )
{
    if(x&1)
    {
        x=x>>1;
        x=x^0xBF9EC099;
    }
    else
    {
        x=x>>1;
    }
    return(x);
}
int main ( void )
{
    unsigned int ra,rb,rc,rd,re;
    unsigned int p;

    unsigned int prand;

    prand=0x12345;

    for(ra=0;ra<20;ra++)
    {
        prand=next_prand(prand);
        p=prand&0xFFFF;
        rb=p/3;
        rc=divthree(p);
        rd=divthree(p+1);
        re=divthree(p+2);

        printf("%u %u ",p,rb);
        printf("(%u %d) ",rc,rc-rb);
        printf("(%u %d) ",rd,rd-rb);
        printf("(%u %d) ",re,re-rb);
        printf("\n");
    }
    return(0);
}

所以运行上面的内容,而不进行舍入......

6931 (6931 0) (6931 0) (6932 1) 
19798 (19798 0) (19798 0) (19799 1) 
20822 (20821 -1) (20822 0) (20822 0) 
10411 (10410 -1) (10411 0) (10411 0) 
21640 (21640 0) (21640 0) (21640 0) 
16241 (16241 0) (16241 0) (16242 1) 
13627 (13627 0) (13627 0) (13628 1) 
12224 (12223 -1) (12224 0) (12224 0) 
6112 (6111 -1) (6112 0) (6112 0) 
3056 (3055 -1) (3056 0) (3056 0) 
12450 (12450 0) (12450 0) (12451 1) 
6225 (6225 0) (6225 0) (6225 0) 
3112 (3112 0) (3112 0) (3113 1) 
1556 (1556 0) (1556 0) (1556 0) 
6274 (6274 0) (6274 0) (6274 0) 
8563 (8563 0) (8563 0) (8563 0) 
4281 (4281 0) (4281 0) (4282 1) 
7642 (7642 0) (7642 0) (7642 0) 
20170 (20169 -1) (20170 0) (20170 0) 
10085 (10084 -1) (10085 0) (10085 0) 

第二组,divthree(n + 1)到目前为止......

通过这个无理数来改进乘法,注意除非使用32位数学运算假设16位数,除法程序如何给出更多精度(不需要那么极端)。

不这样做

unsigned int divthree ( unsigned int x )
{
    unsigned int y;

    y=0;
    //x<<=16;
    while(x)
    {
        x>>=2;
        y+=x;
    }
    //y+=0x8000;
    //y>>=16;
    return(y);
}

不像人们希望的那样准确。

20795 6931 (6928 -3) (6929 -2) (6929 -2) 
59396 19798 (19796 -2) (19796 -2) (19796 -2) 
62466 20822 (20819 -3) (20819 -3) (20820 -2) 
31233 10411 (10408 -3) (10408 -3) (10408 -3) 
64921 21640 (21635 -5) (21635 -5) (21635 -5) 
48725 16241 (16237 -4) (16237 -4) (16237 -4) 
40883 13627 (13622 -5) (13623 -4) (13623 -4) 
36672 12224 (12221 -3) (12221 -3) (12221 -3) 
18336 6112 (6109 -3) (6109 -3) (6109 -3) 
9168 3056 (3053 -3) (3053 -3) (3053 -3) 
37352 12450 (12447 -3) (12447 -3) (12447 -3) 
18676 6225 (6222 -3) (6222 -3) (6222 -3) 
9338 3112 (3109 -3) (3109 -3) (3110 -2) 
4669 1556 (1553 -3) (1553 -3) (1553 -3) 
18823 6274 (6271 -3) (6272 -2) (6272 -2) 
25690 8563 (8560 -3) (8560 -3) (8561 -2) 
12845 4281 (4278 -3) (4278 -3) (4278 -3) 
22927 7642 (7638 -4) (7640 -2) (7640 -2) 
60510 20170 (20165 -5) (20165 -5) (20167 -3) 
30255 10085 (10080 -5) (10082 -3) (10082 -3) 

(可以开始了解浮点单元对你做什么或不做什么)。

如果您尝试换档,则先换两档,然后换三档,然后换四档,这与上面的换档16匹配。

unsigned int divthree ( unsigned int x )
{
    unsigned int y;

    y=0;
    x<<=4;
    while(x)
    {
        x>>=2;
        y+=x;
    }
    y>>=4;
    return(y);
}

根据您的数字或指令集/寄存器,您需要额外的4位余量。

因此,任何时候你想要乘以(或除以)编译时/之前已知的常量,你就可以使用这种简单的移位和加法方法。但如果除法是无理数,你必须处理准确性。