目前共有9篇帖子。
【程序】補碼不恢復餘數除法
1樓 巨大八爪鱼 2017-2-7 13:21

【程序1】
#include <stdio.h>
#include <iostream>
#include <string>


using namespace std;


// 顯示八位二進制數的字元串(C語言編寫)
const char *getcode(char a)
{
    static char str[9];
    char i;
    for (i = 0; i < 8; i++)
    {
        if (a & 0x80)
            str[i] = '1';
        else
            str[i] = '0';
        a <<= 1;
    }
    str[8] = '\0';
    return str;
}


// 補碼不恢復餘數除法(C語言編寫)
// 11.000000~00.111111 (-64~63)
char div(char a, char b, char *r)
{
    char c = 0;
    char i;
    for (i = 0; i < 6; i++) // 尾數有6位, 所以只需要做6步, 不需要像原碼除法那樣額外添加恢復餘數的步驟
    {
        printf("2r%d=%s ", i, getcode(a << 1));
        if (((a ^ b) & 0x80) == 0)
        {
            c |= 1; // 同號商1
            a = (a << 1) + ~b + 1; // A左移後加-B
        }
        else
            a = (a << 1) + b; // 商0, A左移後加+B
        printf("c=%s\n", getcode(c));
        c <<= 1; // 左移後最低位為0
        printf("r%d=%s\n", i + 1, getcode(a));
    }
    *r = a; // 餘數通過參數3返回


    // 對商校正, 對於採用雙符號位的C, 第一符號位始終為0, 第二符號位為求得的假商符
    // 正的假商符00加上11後是真商符11
    // 負的假商符01加上11後是真商符00(捨去進位)
    // 所以應該加上11.000001=0xc1
    // for循環的最後一步的最後, c左移了, 在這裡將最低位恆置1
    c += 0xc1;
    return c; // 返回帶雙符號位的真商
}


// 算式輸出(C++語言編寫)
// 為了簡化代碼, 輸出部分用C++編寫
void output(char a, char b, char c, char r)
{
    string str = getcode(a);
    str += " / ";
    str += getcode(b);
    str += " = ";


    string s2 = getcode(c);
    s2.insert(2, ".");
    str += s2;
    str += "……";
   
    s2 = getcode(r);
    s2.insert(2, ".");
    str += s2;


    cout << endl << "二進制算式: " << str << endl;
    cout << "十進制算式:" << (int)a << " / " << (int)b << " = " << (int)c << "/64……" << (int)r << "/64" << endl;
    cout << "驗算: " << (int)c << "/64 * " << (int)b << " + " << (int)r << "/64 = " << (int)a << endl;
    if (c * b + r == 64 * a) // 把/64從等式左邊移到右邊變成*64, 避免計算機本身的浮點運算問題影響判斷
        cout << "計算結果正確" << endl;
    else
        cout << "計算結果有誤" << endl;
}


int main(void)
{
    char a = 32;
    char b = -40;
    char c, r;
    printf("被除數a=%s\n", getcode(a)); // 小數點位於末尾
    printf("除數b=%s\n", getcode(b));
    c = div(a, b, &r);


    // 以下三句代碼是用C++編寫的
    printf("商c=%s\n", string(getcode(c)).insert(2, ".").c_str()); // 第二符號位後就是小數點
    printf("餘數r=%s\n", string(getcode(r)).insert(2, ".").c_str());
    output(a, b, c, r);


    return 0;
    // 注意: 負數開頭添1, 真值不變
}

2樓 巨大八爪鱼 2017-2-7 13:22
【輸出】
被除數a=00100000
除數b=11011000
2r0=01000000 c=00000000
r1=00011000
2r1=00110000 c=00000000
r2=00001000
2r2=00010000 c=00000000
r3=11101000
2r3=11010000 c=00000001
r4=11111000
2r4=11110000 c=00000011
r5=00011000
2r5=00110000 c=00000110
r6=00001000
商c=11.001101
餘數r=00.001000
二進制算式: 00100000 / 11011000 = 11.001101……00.001000
十進制算式:32 / -40 = -51/64……8/64
驗算: -51/64 * -40 + 8/64 = 32
計算結果正確
3樓 巨大八爪鱼 2017-2-7 13:25
從算式中可以看出,被除數和除數都是整數,而商和餘數都是絕對值小於1的小數。
4樓 巨大八爪鱼 2017-2-7 13:26

【程序2:演算法檢驗】
#include <stdio.h>
#include <math.h>


#pragma warning(disable:4996) // 去掉fopen的警告


int error_cnt = 0;


char div(char a, char b, char *r)
{
    char c = 0;
    char i;
    for (i = 0; i < 6; i++)
    {
        if (((a ^ b) & 0x80) == 0)
        {
            c |= 1;
            a = (a << 1) + ~b + 1;
        }
        else
            a = (a << 1) + b;
        c <<= 1;
    }
    *r = a;
    c += 0xc1;
    return c;
}


void output(FILE *fp, int a, int b, int c, int r)
{
    fprintf(fp, "%d / %d = %d/64……%d/64", a, b, c, r);
    if (c * b + r == 64 * a)
        fputs("(正確)\n", fp);
    else
    {
        fputs("(錯誤)\n", fp);
        error_cnt++;
    }
}


int main(void)
{
    FILE *fp = fopen("演算法檢驗結果.txt", "w");
    char a, b, c, r;
    for (a = -64; a <= 63; a++)
    {
        for (b = -64; b <= -abs(a); b++)
        {
            c = div(a, b, &r);
            output(fp, a, b, c, r);
        }
        for (b = abs(a) + 1; b <= 63; b++)
        {
            c = div(a, b, &r);
            output(fp, a, b, c, r);
        }
    }
    fclose(fp);
    printf("錯誤數: %d\n", error_cnt);
    return 0;
}

5樓 巨大八爪鱼 2017-2-7 13:27

輸出的文件內容如下:
-64 / -64 = 63/64……-64/64(正確)
-63 / -64 = 63/64……0/64(正確)
-63 / -63 = 63/64……-63/64(正確)
-62 / -64 = 61/64……-64/64(正確)
-62 / -63 = 63/64……1/64(正確)
-62 / -62 = 63/64……-62/64(正確)
-62 / 63 = -63/64……1/64(正確)
-61 / -64 = 61/64……0/64(正確)
-61 / -63 = 61/64……-61/64(正確)
-61 / -62 = 63/64……2/64(正確)
-61 / -61 = 63/64……-61/64(正確)
-61 / 62 = -63/64……2/64(正確)
-61 / 63 = -61/64……-61/64(正確)
-60 / -64 = 59/64……-64/64(正確)
-60 / -63 = 61/64……3/64(正確)
-60 / -62 = 61/64……-58/64(正確)
-60 / -61 = 63/64……3/64(正確)
-60 / -60 = 63/64……-60/64(正確)
-60 / 61 = -63/64……3/64(正確)
-60 / 62 = -61/64……-58/64(正確)

……

計算都是正確的。

並且,錯誤數為0。

6樓 巨大八爪鱼 2017-2-7 13:28

這表明,雖然計算過程中商的末尾被恆置1了,但計算結果是精確的,不是近似數。

只要被除數<=除數,輸出都是(正確)。

7樓 巨大八爪鱼 2017-2-7 13:34

現在我們把最開始的十進制算式化為整數形式:

32 / -40 = -51/64……8/64

用字母表示就是:

a / b = c……r

也就是bc+r=a

兩邊同乘t,得

tbc+tr=ta

令A=ta, B=b, C=tc, R=tr

則BC+R=A

這樣我們就得到了另一個除法算式:

A / B = C……R

若t=64,則A=64x32=2048

B=-40, C=-51, R=8

因此,化簡後的算式就是:

2048÷-40=-51……8

8樓 巨大八爪鱼 2017-2-7 13:47
因此,被除數、除數、商和餘數對應的二進制代碼中,小數點的位置可以這樣確定:
被除數a=00.100000 x 2^12 = 00100000000000(也就是說低6位恆為0)
除數b=11.011000 x 2^6 = 11011000(小數點位於末尾)
商c=11.001101 x 2^6 = 11001101(小數點位於末尾)
餘數r=00.001000 x 2^6 = 00001000(小數點位於末尾)
10樓 巨大八爪鱼 2017-2-7 14:01
這個除法器對被除數和除數的要求是:
1.被除數的低6位恆為0,也就是必須是64的倍數。
2.被除數的最高位和次高位必須相同,因此被除數的範圍為-4096~4032。
3.除數的絕對值的64倍必須大於被除數的絕對值(不可以相等,否則算出來的除數和餘數是錯誤的),例如40x64=2560>2048。
4.除數的範圍為-64~63,且不能為0。

計算時將被除數的高8位和除數置入除法器進行運算,即可得到準確的商和餘數。

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
 
 
©2010-2024 Arslanbar [手機版] [桌面版]
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。