程序最美(寻路)

你还在坚持练习你的技术吗?运动员天天训练,音乐家也会演练更难的曲章。你呢?

原码•反码•补码

原码·反码·补码

         本文可以说是比较水,主要是谈一下原码、反码、补码的东西。原码、反码、补码的定义我们这里不做介绍,可以Google之,查询相关Wiki或百度百科。

         我们主要对一个数,如果转换为原码、反码、补码,并且如何由原码、反码、补码转换为原数。下面我们给出几种转换的代码实现,并对代码做一下解释。

         需要说明的是,我们并没有对正数用原码负数用补码进行分开讨论,而是对正负数、无符号数都求了原码、反码、补码。关于原码、反码、补码的内部机制,为什么有产生这些,为什么会用它们,用它们有什么好处,我们也不做介绍,如果感兴趣的话可以参考《深入理解计算机系统》。

         我们先给出相关的程序,然后对程序进行相关阐述。

#include <iostream>
#include <string>
using namespace std;

// 获取一个数的原码
string TrueForm(int _n)
{
    if (_n == 0) // 如果为0,则返回"00"
    {
        return "00";
    }

    // 有符号数求原码,负数和正数一样,只不过最后设置符号位
    int n = _n;
    if (n < 0)
    {
        n = -n;
    }
    string ret;
    char ch;
    while (n != 0)
    {
        ch = n % 2 + '0';
        ret = ch + ret;
        n /= 2;
    }
    if (_n > 0)
    {
        ret = '0' + ret;
    }
    else if (_n < 0)
    {
        ret = '1' + ret;
    }
    return ret;
}

// 对二进制编码进行逐位翻转
string Negation(const string& _s)
{
    string ret;
    for (auto i = 0; i != _s.size(); ++i)
    {
        ret += '1' - (_s[i] - '0');
    }
    return ret;
}

// 求反码
string MinusForm(int _n)
{
    string ret = TrueForm(_n);
    ret = Negation(ret);
    // 符号位保持0正1负
    if (_n >= 0)
    {
        ret[0] = '0';
    }
    else
    {
        ret[0] = '1';
    }
    return ret;
}

// 求补码
// 补码的求解方法是先求反码,然后加1,最后根据正负号,置符号位
// 根据反码求补码
string ComplementForm(int _n)
{
    string ret = MinusForm(_n);
    // 实现二进制加1
    int pos = ret.size() - 1;
    while (pos >= 0)
    {
        if (ret[pos] == '0')
        {
            ret[pos] = '1';
            break;
        }
        else
        {
            ret[pos] = '0';
        }
        --pos;
    }

    if (_n >= 0)
    {
        ret[0] = '0';
    }
    else
    {
        ret[0] = '1';
    }

    return ret;
}

// 根据原码直接进行转换,得到补码
// 原码与补码的关系是从右边数,碰到1后,将1左边的其余字符都进行翻转即得补码
// 111000 原码
// 000111 反码
// 001000 补码
//
// 101010 原码
// 010101 反码
// 010110 补码
// 符号位还是要坚持0正1负
string ComplementFormByTrue(int _n)
{
    string ret = TrueForm(_n);
    int pos = ret.size() - 1;
    while (pos >= 0 && ret[pos] == '0') // 如果为0,则向左继续查找
    {
        --pos;
    }
    // 退出循环后,ret[pos]为1,该位需要保留
    --pos;
    // 对剩下的pos到0进行逐位翻转
    while (pos >= 0)
    {
        ret[pos] = '1' - (ret[pos] - '0');
        --pos;
    }

    // 坚持符号位
    if (_n >= 0)
    {
        ret[0] = '0';
    }
    else
    {
        ret[0] = '1';
    }
    return ret;
}

// 以上是对有符号数的转换,有符号的数有一个符号位
// 下面是关于无符号的,无符号位
string TrueFormUnsigned(unsigned int _n)
{
    if (_n == 0)
    {
        return "0";
    }
    string ret;
    char ch;
    unsigned int n = _n;
    while (n != 0)
    {
        ch  = '0' + n % 2;
        ret = ch + ret;
        n  /= 2;
    }
    return ret;
}

string MinusFormUnsigned(unsigned int _n)
{
    string ret = TrueFormUnsigned(_n);
    ret = Negation(ret);
    // 不涉及符号位
    return ret;
}

// 通过反码求补码
string ComplementFormUnsigned(unsigned int _n)
{
    string ret = MinusFormUnsigned(_n);

    // 实现二进制加1
    int pos = ret.size() - 1;
    while (pos >= 0)
    {
        if (ret[pos] == '0')
        {
            ret[pos] = '1';
            break;
        }
        else
        {
            ret[pos] = '0';
        }
        --pos;
    }
    // 不涉及符号位
    return ret;
}

// 通过原码求补码
string ComplementFormUnsignedByTrue(unsigned int _n)
{
    string ret = TrueFormUnsigned(_n);
    // 从右到左求出为1的pos
    int pos = ret.size() - 1;
    while (pos >= 0 && ret[pos] == '0')
    {
        --pos;
    }
    // 退出循环时,pos指向最右边的1,本位的1保留
    --pos;

    // 逐位翻转
    while (pos >= 0)
    {
        ret[pos] = '1' - (ret[pos] - '0');
        --pos;
    }
    // 不涉及符号位
    return ret;
}

// 以上是对数到原码、反码、补码的转换
// 下面讨论一下原码、反码、补码到数的转换
int TrueFormToInt(const string& tf)
{
    int ret = 0;
    for (int i = 1; i != tf.size(); ++i)
    {
        ret = ret * 2 + tf[i] - '0';
    }
    if (tf[0] == '1')
    {
        ret = -ret;
    }
    return ret;
}

// 将其转换为原码,再计算
int MinusFormToInt(const string& mf)
{
    int ret = 0;
    string tmp = Negation(mf);
    if (mf[0] == '0')
    {
        tmp[0] = '0';
    }
    else
    {
        tmp[0] = '1';
    }
    ret = TrueFormToInt(tmp);
    return ret;
}

// 将其转换为原码,再计算
// 也可将其转换为反码再计算
// 补码转换为原码有两种方式:
// 1.为将其减1,然后取反,即原码求补码的逆过程
// 2.为将其翻转,然后加1
// 111000 原码  取反
// 000111 反码  减1
// 001000 补码
//
// 110111 取反
// 111000 加1
//
//
// 101010 原码  取反
// 010101 反码  减1
// 010110 补码
//
// 101001 取反
// 101010 加1

// 第1种方式很好理解,即是逆过程
// 第2种方式是因为原码最右的1之前的数都都翻转,其后(包括该1)都保持不变
// 先翻转,则导致前面的翻转为原码,后面的与原码相反
// 然后再加1,导致后面的都翻转了,进而和原码一致
// 另外也可以只通过减1的方式得到反码,再由反码求得值,本质上与第1种方法一样
// 
int ComplementFormToInt(const string& cf)
{
    string tmp = cf;
    // 对tmp进行减1操作
    int pos = tmp.size() - 1;
    while (pos >= 0)
    {
        if (tmp[pos] == '1')
        {
            tmp[pos] = '0';
            break;
        }
        else
        {
            tmp[pos] = 1;
        }
        --pos;
    }
    if (cf[0] == '0')
    {
        tmp[0] = '0';
    }
    else
    {
        tmp[0] = '1';
    }
    return MinusFormToInt(tmp);
}

// 以上是将原码、反码、补码转换为有符号数
// 下面我们将其转换为无符号数
unsigned int TrueFormToUnsignedInt(const string& tf)
{
    unsigned int ret = 0;
    for (int i = 0; i != tf.size(); ++i)
    {
        ret = ret * 2 + tf[i] - '0';
    }
    return ret;
}

// 反码求无符号数
// 通过原码求
unsigned int MinusFormToUnsignedInt(const string& mf)
{
    string tmp = Negation(mf);
    return TrueFormToUnsignedInt(tmp);
}

// 补码求无符号数
// 通过反码求
unsigned int ComplementFormToUnsignedInt(const string& cf)
{
    string tmp = cf;
    // 对tmp减1操作
    int pos = tmp.size() - 1;
    while (pos >= 0)
    {
        if (tmp[pos] == '1')
        {
            tmp[pos] = '0';
            break;
        }
        else
        {
            tmp[pos] = '0';
        }
        --pos;
    }
    return MinusFormToUnsignedInt(tmp);

    //// 另一种实现tmp减1的操作
    //while (pos >= 0 && tmp[pos] == '0')
    //{
    //    --pos;
    //}
    //if (pos >= 0)
    //{
    //    tmp[pos] = '0';
    //}
}


int main()
{
    int n = 0;
    string s;

    while (cin >> n)
    {
        cout << n << endl;
        cout << TrueForm(n) << endl;
        cout << MinusForm(n) << endl;
        cout << ComplementForm(n) << endl;
        cout << ComplementFormByTrue(n) << endl;
        cout << endl;

        unsigned int o = (unsigned int)n;
        cout << TrueFormUnsigned(o) << endl;
        cout << MinusFormUnsigned(o) << endl;
        cout << ComplementFormUnsigned(o) << endl;
        cout << ComplementFormUnsignedByTrue(o) << endl;
        cout << endl;

        cin >> s;
        cout << TrueFormToInt(s) << endl;
        cout << MinusFormToInt(s) << endl;
        cout << ComplementFormToInt(s) << endl;
        cout << endl;

        cout << TrueFormToUnsignedInt(s) << endl;
        cout << MinusFormToUnsignedInt(s) << endl;
        cout << ComplementFormToUnsignedInt(s) << endl;
        cout << endl;
    }

    return 0;
}

         代码中有一些注释解释。

         我们的程序主要实现了如下几个函数:

函数

作用

TrueForm

将有符号数转换为原码

MinusForm

将有符号数转换为反码

ComplementForm

将有符号数转换为补码,通过反码的方式

ComplementFormByTrue

将有符号数转换为补码,通过原码的方式

TrueFormUnsigned

将无符号数转换为原码

MinusFormUnsigned

将无符号数转换为反码

ComplementFormUnsigned

将无符号数转换为补码,通过反码的方式

ComplementFormUnsignedByTrue

将无符号数转换为补码,通过原码的方式

TrueFormToInt

将原码转换为有符号数

MinusFormToInt

将反码转换为有符号数

ComplementFormToInt

将补码转换为有符号数,通过反码的方式

TrueFormToUnsignedInt

将原码转换为无符号数

MinusFormToUnsignedInt

将反码转换为无符号数

ComplementFormToUnsignedInt

将补码转换为无符号数,通过反码的方式

Negation

逐位取反

 

         求一个数的二进制形式有很多种方法,我们用的就是最为经典的除二取余法。除此之外还可以调用库函数、运用位运算(本质上还是除二取余法)等等。

         有符号数中,最左边的第1位为符号位,0正1负。无符号数无符号位。对于有符号的0,我们直接将其返回“00”,第一位是符号位,第二位为值,虽然为0,还是要占一位。对于无符号数的0,我们之间返回“0”。

         反码的计算即使将原码各位取反,保留符号位。

         补码的计算有两种方式,一是通过原码的取反,加1,并保留符号位。另一种方法是根据原码来求,对从最右边1的左边所有位取反。

         对二进制数进行加1或减1操作数,只需判断当前为是否满足条件即可(这里有两种实现方式,A[pos]和pos)。逻辑与大数加减算法类似。

         逐位翻转的操作是:a = ‘1’ – (a – ‘0’),如果a = ‘0’,则 a = ‘1’ – (‘0’ – ‘0’) = ‘1’;如果a = ‘1’,则a = ‘1’ – (‘1’ – ‘0’) = ‘1’ – 1 = ‘0’。

         求由补码到原码的方法有两种,第一种是减1取反,第二种是取反加1。具体可以参考代码中注释247~272行。

 

         总结

         以上我们对原码、反码、补码与有符号数、无符号数之间的相互转换进行了讨论和实现。

posted on 2013-09-26 22:43  unixfy  阅读(614)  评论(0编辑  收藏  举报

导航