信息的表示和处理

现代计算机存储和处理的信息以二值信号表示。这些微不足道的二进制数字,或者称为位(bit),奠定了数字革命的基础。孤立的讲,单个的位不是非常有用。然而,当把位组合在一起,再加上某种 解释,即给不同的可能 位模式 赋予含义,我们就能表示任何有限集合的元素。


一、数值的表示

1. 1 数值的编码

数值的类型主要分为2种:整数 和 浮点数。 编码的方式主要分为3种:无符号编码、补码编码 和 浮点数编码。

  • 无符号 编码基于传统的二进制表示法,表示大于等于0的数字。

  • 补码 编码是表示有符号整数的最常见的方式。

  • 浮点数 编码是表示实数的 科学计数法 的以二位基数的版本,所以浮点数不能精确保存,而且由于表示的精度有限,浮点运算是不可结合的。

无符号数采用原码存储,有符号数采用补码存储。正数的补码就是他本身,负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1. (即在反码的基础上+1)

[+1] = [0000 0001]原 = [0000 0001]反 = [0000 0001]补

[-1] = [1000 0001]原 = [1111 1110]反 = [1111 1111]补

计算机的表示法是用有限数量的位来对一个数字编码,因此,当结果太大以至不能表示时,某些运算就会导致 溢出。例如,大多数计算机(使用32bit表示整数数据int),计算表达式 200*300*400*500 会得出结果 -884 901 888。

整数的表示虽然只能编码一个相对较小的数值范围,但是这种表示是精确的;而浮点数虽然可以编码一个较大的数值范围,但是这种表示只是近似的。

1.2 进制转换

编写程序的一个常见任务是在位模式的十进制、二进制、八进制和十六进制表示之间人工进行转换。

1.2.1 二进制,八进制 与 十六进制之间的转换

下面以二进制与十六进制之间的转换为例,其他情况在代码中非另外说明:

  • 二进制 -> 十六进制

    二进制向十六进制转换时,四位转换成十六进制的一位,运算的顺序是从低位向高位依次进行,高位不足四位用零补。以“1110011”转换成十六进制为例,如下图所示:


这里写图片描述

转换的结果为:1001011101 == 0X25D

// 二进制转换为十六进制
string bin2hex(string two)
{
    string res; // 转换结果

    int i = two.length() - 1; // 从最低位开始转换
    int j = 0;
    int sum = 0; // 每4位的和
    while (i >= 0)
    {
        j = 0;
        sum = 0;
        while (j<4  && i >= 0)  // 把 “j < 4” 改成 “j < 3” 就变成了“二进制 -> 八进制”
        {
            sum += (two[i] - '0') * pow(2, j); // 2^j

            i--;
            j++;
        }

        if (sum <= 9)
            res += sum + '0';
        else
            res += sum - 10 + 'A';
    }

    return res;
}
  • 十六进制 -> 二进制

    十六进制向二进制转换,就是把十六进制的一位转换成二进制的四位,注意运算的顺序是从低位向高位依次进行。同样以十六进制“0X25D”为例,如下图所示:


这里写图片描述

// 十六进制 -> 二进制
string hex2bin(string hex)
{
    string res; // 转换结果

    int i = hex.length() - 1; //从低位开始
    int num = 4; //用于保证每个16进制位用4个二进制位来表示
    while (i >= 0)
    {
        char c = hex[i];
        int val;

        if (c >= '0' && c <= '9')
        {
            val = c - '0';
        }
        else if (c >= 'A' && c <= 'F')
        {
            val = c - 'A' + 10;
        }
        else
        {
            return string();
        }

        num = 4;

        while (val)
        {
            res += val % 2 + '0';
            val = val / 2;
            num--; 
        }

        while (num != 0) //如果转换过程中,已经占用4位,则不用在高位补0
        {
            res += '0';
            num--;
        }

        i--;
    }

    return res;
}
1.2.2 十进制转换为任意进制

十进制转换为其他进制较为简单,只要依次取得余数即可。注意,越先出来的余数是越低位。

string ten2any(int ten, int rd) // rd : 目标进制
{
    string res;
    int val;

    while (ten)
    {
        val = ten % rd; 

        if (val >= 10)
            res += val - 10 + 'A';
        else
            res += val + '0';

        ten = ten / rd;
    }

    return res;
}
1.2.3 任意进制转换为十进制

二进制数 1010 转换为十进制的过程是: (0 * 2^0) + ( 1 * 2^1) + (0 * 2 ^ 2) + (1 * 2 ^3) = 10。

// 任意进制 -> 十进制
int any2ten(string any, int rd)
{
    int i = any.length() - 1;
    int j = 0;
    int sum = 0;

    while (i>=0)
    {
        if (any[i] <= '9' && any[i] >= '0')
            sum += (any[i] - '0') * pow(rd, j);
        else
            sum += (any[i] - 'A' + 10) * pow(rd, j);

        j++;
        i--;
    }

    return sum;
}

1.3 大端序 & 小端序

小端序指数据的低字节保存在内存的低地址中,而数据的高字节保存在内存的高地址中 。 大端序刚好相反。

如下一段代码:

int a = 4 ;     // 0000 0100
int b = 257 ;   // 0000 0001 0000 0001

根据 小端模式 的规定,低字节存储在低地址中,所以对于a = 4 , 低8位将放置在a所占用的4个地址中的最低位。如下:

这里写图片描述

对于以上代码,如果按照 大端序 存储的话,则如下:

这里写图片描述

1.3.1 判断CPU的?端序
int a = 1;
if ((char)a == 1) //取最低地址的一个字节
    cout << "小端序" << endl;
else
    cout << "大端序" << endl;
1.3.2 说明

更多关于端序的讨论,可参考 C++ 笔试题集锦(1) - 问题4 。

1. 4 无符号数 & 有符号数

有符号数到无符号的隐式强制类型转换经常导致某些非直观的行为。而这些非直观的行为经常导致程序出错。

如下两个示例:

float sum_elements(float a[], unsigned len)
{
    int i ;
    float result = 0;

    for(i = 0; i <= len - 1; i++)
    {
        result += a[i];
    }
    return result;
}

当参数len == 0时,运行这段代码本应该等于0。但是 len - 1 == 4294967295; 而且int 的表示范围是 -2147483648~2147483647 , 所以当 i 增长到 2147483647 时,继续+1将变为-2147483648,如此循环下去,所以这个for循环本身就是死循环。另一方面,i 是负数或者太大都会造成a[]数组访问越界。

size_t strlen(const char *s); // 注意 : typedef unsigned int size_t 

int strlonger(char * s, char * t)
{
    return strlen(s) - strlen(t) > 0;
}

这个函数并不能实现比较两个字符串长度的功能。当s的长度更小时,strlen(s) - strlen(t) 将会因为借位得到一个大于0的值。


二、类型转换

当等号两边的类型不一致时,将自动发生隐式类型转换。

2.1 相同字长的整数转换

C语言允许无符号数和有符号数之间的转换。

转换的原则: 底层的位模式保持不变,改变解释这些位的方式。

char v = -1;                // 1111 1111 -> -1
unsigned char uv = v;       // 1111 1111 -> 255
char v2 = uv;               // 1111 1111 -> -1

unsigned char uc = 0;       // 0000 0000 -> 0
uc = uc - 1;                // 1111 1111 -> 255  借位,重新解读位模式

char c = 127;               // 0111 1111 -> 127
c = c + 1;                  // 1000 0000 -> -128 进位,重新解读位模式

2.2 不同字长的整数转换

2.2.1 短字长 -> 长字长
  • 零扩展

    将一个无符号数转换为一个更大的数据类型,我们只需要简单地在表示的开头添加0。

unsigned char -> short

unsigned char uc = 128; // 1000 0000           -> 128
short s = uc;           // 0000 0000 1000 0000 -> 128 添加0
  • 符号扩展

    将一个有符号数转换为一个更大的数据类型,我们只需要简单地在表示的开头添加符号位。

char -> short

char uc = -128;     // 1000 0000           -> -128
short s = uc;       // 1111 1111 1000 0000 -> -128 添加符号位 1

char uc2 = 127;     // 0111 1111           -> 127
short s2 = us2;     // 0000 0000 0111 1111 -> 127 添加符号位 0
2.2.2 长字长 -> 短字长

当把一个更大数据类型的值 赋给 一个较小数据类型的值时,将发生 截断

short s = 128;  // 0000 0000 1000 0000  -> 128
char c = s;     // 1000 0000            -> -128 只获得低8位

short s = 384;  // 0000 0001 1000 0000  -> 384
char c = s;     // 1000 0000            -> -128 只获得低8位

2.3 说明

更多关于类型转换示例,参考 C++ 类型转换


三、整数运算

TODO


四、浮点数

TODO

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值