您的位置:首页 > 其它

信息的表示和处理

2016-05-10 19:38 281 查看
现代计算机存储和处理的信息以二值信号表示。这些微不足道的二进制数字,或者称为位(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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: