2.数据的表示

2.1

2.1.1 10进制和2进制

转换的概念在数据表示里的反映,高级语言写的程序,而指令只能指定数据放在某个寄存器里,或者放在存储器里。

2.1.2

数据在计算机里的表示。

首先要会进制转换。整数比较容易,小数会有个问题,有些小数无法精确用二进制表示。

此外还有小数点的约定,小数点约定在固定位置为定点书,小数点位置可变,就是浮点数。

定点小小数用来表示浮点数的尾数部分。

定点整数用来表示整数,分带符号数和无符号数。

任何实数都可以表示为:X=(1)s×M×REX = (-1)^s \times M \times R^E

其中s用来确定符号,M是二进制定点小数,称为X的尾数,E为定点整数。因此任何一个小数都可以用两个定点整数来表示。

到这里,又要研究定点整数来怎么表示了。

2.2

数据表示的三要素

  • 进位计数制

  • 定、浮点表示

  • 如何用二进制编码

不同计数制之间的转换

定、浮点数表示(解决小数点的问题)

  • 定点整数,定点小数

  • 浮点数(一个定点小数和一个定点整数来表示)

接下来问题到了如何表示一个定点数的问题,即定点数的编码

2.2.1

编码有原码、反码、补码

原码只是用最高位来表示符号,人容易理解,但是有问题

  • 0表示不唯一

  • 加减运算不统一

  • 要额外处理符号位

还有移码,很简单,将每个数值加上一个偏置常数。移码表示浮点数的阶

2.2.2 补码和模运算

一个模运算系统,一个数和对模取余等价。

指针表是模12系统,(10+8)%12=6=10-4

也即在模12的系统里-4=8

一个负数的补码等于模减负数的绝对值

使用补码可以实现加和减的统一。

计算机内数据保存都是补码形式,通过真值算补码:补码=真值+模值

2.2.3 补码和真值的对应关系

假定机器数有n位,

补码=模值+真值

对于正数来说,就是本身。对于负数,数值部分按位取反再加一。

那么给出补码如何求真值,有公式,当然按照上面倒着来更容易。

2.3

有了编码基础的知识,下面来看C语言里整数在机器里的存储方式

整数有char short int / long int / long long int

类型可以声明为singed / unsigned

指针本质也是个整数

2.3.1 整数

整数在机器里以比特流的形式保存,描述比特位的时候,用最左位或最右位来描述有歧义(有的机器最高有效位在左,有的机器在右)。

因此用最高有效位MSB,最低有效位LSB来描述。

无符号数很容易。

带符号数,机器里用MSB来表示符号。

有三种定点编码方式

  • 原码,定点小数,表示浮点数尾数

  • 移码,定点整数,表示浮点数的阶(指数)

  • 补码,用来表示带符号整数

用补码的优势在前面又说,可以使得加减法统一起来。

关于不同标准下对于特殊数据出现的bug,参考教材。

2.3.1 浮点数

浮点数的类型float(32bit),double(64bit),还有long double

科学计数法可以把任何一个十进制的数写成指数形式,规格化的是小数点前就一位整数。当然并不唯一。

表示方法

2.3.2 ieee 754

IEEE 754标准

早期,不同机器自己约定自己的,导致一台机器上的程序换到另一台机器会出问题,知道80年代初都是这个样子。1985年才有了754标准,现在通用计算机都使用这个标准。

对于单精度

阶码8bit,可表示-126~127,使用移码表示,码值为1~254,偏移值127,全0全1有特殊含义。

双精度浮点数,阶码11bit,尾数52bit,阶码偏移值1023

所以一个机器码转化为真值:

sp=(1)s×(1+尾数)×2阶码127sp = (-1)^s \times (1+尾数) \times 2^{阶码-127}

举例子,一个float类型的变量,内存里的值为BEE00000H,真值为

此外还有一些特殊情况,

全零指数和全零尾数表示零,正负零。

此外还可以表示正负无穷大,浮点数除以0不会像整数一样有异常,而是用无穷。

无穷用全1阶码,全零尾数来表示。

此外还有“不是一个数“的表示NaN,全1阶码,尾数非零。这种情况会在负数开根号等情况出现,可以辅助调试程序

此外还有全零阶码的情况,表示非规格化数。

最小的规格化数为21262^{-126}, 在这个数到0之间,可以表示非规格化的数。

此外浮点可以表示的数是有限的,因此存在精度问题

2.5 非数值数据

逻辑关系的真假,逻辑量一个bit就可以表示了,N位二进制数可以表示N个逻辑数据。

逻辑数据和数值数据在形式上没有区别,都是01序列,在运算时靠指令来区别。操作码是ADD,那就是数值数据。

计算机也可以处理字符,西文字符用有限字母就可以拼写所有单词,因此只需要处理有限个字符。常用的就是ascii码。

对字符串的操作有传送、比较等。

ascii用7个bit编码,要特别熟悉的编码,空格,换行,回车。

汉字的表示就比较复杂了,一个字是一个方块图形,这就带来了表示和存储的问题。

汉字编码形式有输入码,如输入法拼音,五笔。还有内码,用于在系统里进行存储、查找、传送等处理。还有字模点阵或轮廓描述,用于显示。

西文字符没有输入码,键盘直接输进去就是,西文字符的内码就是ascii码,也有点阵描述。

比较早的汉字编码为GB2312,这个国标有三部分,字母数字符号英文俄文罗马字母拼音等不是汉字的字符;一级常用汉字3755个,按照汉语拼音排列;二级常用汉字3008个。

汉字有区位码,码表94行94列,行号为区号,列号为位号,各占7位。用14位就可以指出汉字在码表中的位置。

每个汉字的区号和位号各自加上32(20H)就得到了国标码。显然汉字内码需要2个字节。为了与ascii码区别,把最高位置1。

此外还有多媒体信息,图形图像音频视频等信息。图形用构建图形的直线曲线左边点控制点等来描述;图像用像素点的数值数据来表示;音频通过采样量化获得;视频信息描述随时间变化的图像;音乐信息MIDI记录乐器的演奏编码信息。

2.6 数据的宽度和存储容量

bit是计算机里的最小单位

信息的基本计量单位Byte

  • 现代计算机,存储器按字节编址

  • 字节是最小可寻址单位

  • LSB表示最低有效字节

此外还有“字”的概念,IA32中字是16位,这也是从8086延续下来的。

  • DWORD 32bit

  • QWORD 64bit

字长是定点数据通路的宽度,即计算机内部总线的宽度,或者ALU运算的宽度,寄存器的宽度,都是一样的。字和字长没啥关系,

x86结构字16bit,字长32bit;MIPS结构字32bit,字长32bit

数据量的度量单位KB、MB、GB、TB

通信里的带宽的单位kb/s、Mb/s、Gb/s、bps,区别Byte和bit

高级语言的不同数据类型,在不同机器上实现出来的长度可能不同。

2.7 数据存储时的字节排列

存储器编制方式,大端和小端。

80年代开始,所以通用计算机都是按字节编址。因此一个数据可能占据多个存储单元,如double要占用4个byte

int也要存放4个Byte,那么存放这个数的地址指的是最大地址还是最小地址呢,一般来说,是最小地址。看有个问题,最小地址放最低字节,也可以反过来。

存放方式关系到取数指令是否可以正确的取出数据。

高址高位,小端;高址地位,大端。

MIPS架构:高址高位,小端人看着费劲,但是强制类型转换直接截断就行

8086:高址地位,大端人看着比较容易

判断大小端可以通过C语言特性写个程序执行来

#include <stdio.h>

void main()
{
  union NUM
  {
    int a;
    char b;
  } num;
  num.a = 0x12345678;
  printf("%x",num.b);
}

输出0x12是小端,输出0x78是大端。

还有这种语法也可以实现

int a = 0x12345678;
char b;
b = *(char *)&a

同样通过b的值来判断大小端。

再个举例子,假设小端机器,一条指令的地址为1000,这条指令的汇编形式为mov ax,0x12345

mov的操作码为40H,寄存器ax编号1,bx编号2,立即数32位,那么存放顺序

(1000)40 12 45 23 01 00 (1005)

在大端机器里的存放方式

(1000)40 12 00 01 23 45(1005)

同样的,看一个反汇编的例子,由反汇编器生成的IA32机器器

80483d2: 89 85 a0 fe ff ff mov %eax, 0xfffffea0(%ebp)

这条指令占了6个Byte。

那么可以看出立即数如果是有符号数的话真值为(懒得算了),存放地址的d4,

intel处理器小端方式,高址高位,不方便人看,方便机器使用。

有的机器是大端,有的机器是小端,这就有了字节交换的问题,虽然每台机器上都是一样的,但是程序在机器之间移植或数据通信时会出问题。

音频视频图像等文件的处理都涉及到字节顺序的问题。

小端:gif、

大端:Photoshop、jpeg

最后更新于