数据的表示

来源:互联网 发布:win系统传文件到mac 编辑:程序博客网 时间:2024/05/22 17:16


1、关于进制

十进制

0

1

2

3

4

5

6

7

八进制

0

1

2

3

4

5

6

7

十六进制

0

1

2

3

4

5

6

7

二进制

0000

0001

0010

0011

0100

0101

0110

0111

十进制

8

9

10

11

12

13

14

15

八进制

10

11

12

13

14

15

16

17

十六进制

8

9

A

B

C

D

E

F

二进制

1000

1001

1010

1011

1100

1101

1110

1111

 

2

每台计算机都有一个字长(word size,指明整数和指针数据的标称大小(nominal size。因为虚拟地址是以这样的一个字来编码的,所以字长决定了系统中虚拟地址空间的大小。

 

3数据大小

C语言中不同的数据类型分配的字节数依赖于机器和编译器。下表是32位和64位机器的典型值。

C声明

32位机器

64位机器

char

1

1

short int

2

2

int

4

4

long int

4

8

long long int

8

8

char *

4

8

float

4

4

double

8

8

long double

 

 

 

4寻址和字节顺序

对于跨越多字节的程序对象,需建立两规则:这个对象的地址是什么,如何在存储器中排列这些字节。在几乎所有的机器上,多字节对象都被存储为连续的字节序列,对象的地址为所使用字节中最小的地址。排列表示一个对象的字节有两个通用的规则:最低有效字节在最前面的方式,称为小端法(little endian;最高有效字节在最前面的方式,称为大端法(big endian

假设变量x类型为int,位于地址0x100处,它的十六进制表示为0x01234567。地址范围为0x100~0x103的字节,其排列顺序依赖于机器的类型。

端法

 

地址

...

0x100

0x101

0x102

0x103

...

数据

...

67

45

23

01

...

 

端法

 

地址

...

0x100

0x101

0x102

0x103

...

数据

...

01

23

45

67

...

typedef unsigned char *byte_pointer;

 

//=========================================

// 显示不同类型对象的字节表示

//=========================================

void show_bytes(byte_pointer start, int len)

{

for (int i = 0; i < len; i++)

printf(" %.2x", start[i]);

printf("\n");

}

 

void show_int(int x)

{

show_bytes((byte_pointer)&x, sizeof(int));

}

 

void show_float(float x)

{

show_bytes((byte_pointer)&x, sizeof(float));

}

 

void show_pointer(void *x)

{

show_bytes((byte_pointer)&x, sizeof(void *));

}

对指针的类型强制转换不会改变真实的指针,只是告诉编译器以新的数据类型来看待被指向的数据。

 

5、布尔代数

~

 

 

&

0

1

 

|

0

1

 

^

0

1

0

1

0

0

0

0

0

1

0

0

1

1

0

1

0

1

1

1

1

1

1

0

布尔环(Boolen ring

a ^ a = 0 ===> (a ^ b) ^ a = b在许多应用中可以使用该属性。

void inplace_swap(int *x, int *y)

{

*y = *x ^ *y;

*x = *x ^ *y;

*y = *x ^ *y;

}

 

6、整形的表示

6.1、无符号数的编码

假如一个整数类型的数据有w为,我们可以将位向量写成,表示整个向量,或者写成,表示向量中的每一位。把看做一个二进制表示的数,就获得了的无符号表示。用函数B2UwBinary to Unsigned来表示:

 

无符号数的二进制表示有一个很重要的属性,就是每个介于0~2w-1之间的数都有唯一一个w位的值编码。函数B2U是一个双射(bijection)。

 

6.2、补码编码

最常见的有符号数的计算机表示方式就是补码(twos-complement形式在这个定义中,将字的最高有效位解释为负权(negative weight。用函数B2TwBinary to Twos-complement来表示:

 

最高有效位xw-1也称为符号位,它的权重为-2w-1。符号位设置为1时,表示值为负,而当设置为0时,值为非负。函数B2Tw是一个双射,每个介于-2w-1~2w-1-1之间的数都有唯一一个w位的值编码。补码的范围是不对称的:

6.3、有符号数的其他表示方法

反码(Ones Complement:除了最高有效位的权是-(2w-1-1)而不是-2w-1,其它和补码一样的:

 

源码(Sign-Magnitude:最高有效位是符号位,用来确定剩下的位应该取负权还是正权:

 

 

这两种表示方法都有一个奇怪的属性,即对于数字0都有两种不同的编码方式。 

 

6.4、有符号数和无符号数之间的转换

对于多数C语言的实现而言,处理同样字长的有符号数和无符号数之间的相互转换的一般规则是:数值可能会变,但是位模式不变。

有符号转换成无符号:

 

无符号转换成有符号:

 

 

 

6.5、扩展一个数字的位表示

无符号的数采用零扩展(zero extension,补码的数采用符号扩展(sign extension

补码(有符号的数)满足下列等式:

 

6.6、截断数字

无符号数的截断结果是:

 

补码数字的截断结果是:

 

 

7、整形运算

7.1、无符号的加法

无符号的运算可以被视为一种模运算形式。

定义参数xy运算如下:

 

 

 

判断无符号数xy相加是否溢出的C函数:

int uadd_ok(unsigned int x, unsigned int y)

{

unsigned int sum = x + y;

 

return sum > x;

}

 

7.2、补码的加法

定义参数xy运算如下:

 

两个数的w位补码之和与无符号之和有完全相同的位级表示。

 

 

 

判断补码数xy相加是否溢出的C函数:

int tadd_ok(int x, int y)

{

int sum = x + y;

int neg_over = x < 0 && y < 0 && sum >= 0;

int pos_over = x > 0 && y > 0 && sum > 0;

 

return !neg_over && !pos_over;

}

 

7.3、无符号的乘法

W位无符号乘法运算的结果为:

 

7.4、补码的乘法

w位补码乘法运算的结果为:

 

乘法运算的位级表示都是一样的。也就是,给定长度为w的位向量,无符号乘积的位级表示补码乘积的位级表示是相同的。

 

 

7.5、乘以常数与除以2的幂

由于整数乘法比移位和加法的代价要大得多,许多C编译器试图以移位、加法和减法的组合来消除很多整数乘以常数的情况。

整数的除法,当需要舍入时,采用向零取整的方法,而算法右移的结果,相当于向下取整。当,整数除法的结果是(向上取整),与右移的(向下取整)不同,因此在移位之前可以利用下面属性对x进行“偏置”(biasing

对于整数和任意,有

对于使用算术右移的补码机器,C表达式:

(x < 0 ? (x + (1<<k)-1) : x) >> k

 

总结

计算机执行的“整数”运算实际上是一种模运算形式。表示数字的有限字长限制了可能的值的取值范围,结果运算可能溢出。补码表示提供了一种既能表示负数又能表示正数的灵活方法,同时使用了与执行无符号算术相同的位级实现,这些运算包括加法、减法、乘法、甚至除法,无论运算数是以无符号形式还是以补码形式表示,都有完全一样或者非常类似的位级行为。

0 0
原创粉丝点击