2

私は次の命令を書こうとしています:

AC <-- AC / 3.

2 による除算を実行するために算術右シフトを使用できることはわかっていますが、マイクロプログラムされた制御で可能な命令 (マイクロ命令) のみを使用して、奇数による除算を実行するにはどうすればよいでしょうか。

ありがとうございました

4

3 に答える 3

3

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<<2 + 1<<0 なので、変数 n*5 = (n<<2) + (n<<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<<2) + (n<<0) を意味する場合、ゼロ以外のビットは 2^0 と 2^2 の位置にあるためです。次に、上記の 5 で行ったように乗算を行うと、小数点以下の桁数は関係ありません。0.1 は 2 の -1 乗、0.01 は 2 の -2 乗など、2 進数の 1.01 × N は (n<<0) + (n>>2) になります。

そして最後に 0.0101010101 で近似される 1/3 を掛けると....つまり

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

他の誰かが指摘したように、ループでそれを行うことができます。

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

基数 10 の場合と同様に、3 の因数を持つもので割ると、無限に繰り返される数が得られます。基数 2 の無限の繰り返し数でも同じ問題があります。基数 10 のように、桁数に応じて 0.6666666 を 0.666667 にしたい場合もありますが、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>>=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) 

その 2 番目のセット、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) 

(浮動小数点ユニットが何をするか、何をしないかを理解し始めることができます)。

1、2、3、4 とシフトしてみると、これは上のシフト 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 ビットのヘッドルームが必要になります。

したがって、コンパイル時またはコンパイル前に既知の定数で乗算 (または除算) したいときはいつでも、シフトと加算のこの単純な方法を使用できます。ただし、除算が無理数によるものである場合は、精度に対処する必要があります。

于 2013-09-15T22:25:32.063 に答える