目前共有9篇帖子。 内容转换:不转换▼
 
点击 回复
400 8
【程序】补码不恢复余数除法
一派护法 十九级
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位和除数置入除法器进行运算,即可得到准确的商和余数。

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
(快捷键:Ctrl+Enter)
 

本帖信息

点击数:400 回复数:8
评论数: ?
作者:巨大八爪鱼
最后回复:巨大八爪鱼
最后回复时间:2017-2-7 14:01
 
©2010-2024 Arslanbar Ver2.0
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。