第二章 信息的表示和处理

  • 信息存储
    • 十六进制
      • 十六进制的表示
      • 十六进制与二进制、十进制的转化
    • 字数据大小
    • 寻址和字节顺序
    • 表示字符串
    • 表示代码
    • 布尔代数
    • C语言中相关运算
      • 位级运算:&,|,~
      • 逻辑运算:&&,||,!
      • 移位运算:<<,>>
  • 整数表示
    • 整形数据表示
    • 编码
      • 无符号数(Unsigned)
      • 补码(Two's-Complement)
      • 无符号数和有符号数之间的转换
      • C语言中无符号数与有符号数的转换
    • 扩展位表示
    • 截断数字
  • 整数运算
    • 加法
      • 无符号加法
      • 补码加法
    • 取非
      • 无符号数
      • 补码
    • 乘法
      • 无符号数
      • 补码
    • 乘以常数
      • 与2的幂相乘
      • 与任意常数相乘
    • 除以2的幂
      • 无符号数
      • 补码
  • 浮点数
    • 二进制小数
    • IEEE浮点表示
      • 规格化
      • 非规格化
      • 特殊值
        • 无穷大
        • NaN(Not-a-Number)
    • 舍入规则
    • 浮点运算
      • 加法
      • 乘法
    • 类型转换

信息存储

  • 大多数计算机以字节(1 byte = 8 bits)作为最小可寻址的内存单位。

  • 虚拟内存:机器级程序将内存视为字节数组。

  • 地址:标识每个字节的唯一数字。

  • 虚拟地址空间[1]:所有可能的地址集合。

十六进制

十六进制的表示
  • 前缀0x/0X,字母可用大写A - F或小写a - f

    记住A,C,F的十进制值

十六进制与二进制、十进制的转化
  • 十六进制&二进制:四位二进制转化为一位十六进制,位数缺少时整数部分左侧补零,小数部分右侧补零。

  • 十六进制&十进制:乘除法

    2的非负整数n次幂转化为十六进制:n = i + 4 * j (i = 1, 2, 3)时,十六进制表示为2的 i 次方后有 j 个0

字数据大小

  • 字长决定了虚拟地址空间的最大大小:字长w位,虚拟地址范围为0 ~ 2^w - 1,即可以访问2^w个字节

  • 程序时32位还是64位取决于它是如何编译的。

2-1.png

寻址和字节顺序

  • 多字节对象被存储为连续的字节序列。

  • 字节存储顺序:一个数据类型内部字节的存储顺序

    • 大端法:高位字节优先存储

    • 小端法:低位字节优先存储

      大端法(big endian)和小端法(little endian)取自《格列佛游记》中打破鸡蛋的方式。原文暗讽英法两国持续的战争,而这里则暗示选择两种存储顺序没有技术上的理由,无谓好坏。

  • 需要注意字节存储顺序的三种情况

    • 不同机器通过网络传送二进制数时,由小端法机器传送给大端法机器的字节顺序会被认为是反序,反之亦然。此时应统一编写网络应用程序的字节顺序规则,由发送方机器将其内部表示先转化为网络标准,再由接收方机器将收到的网络标准转化为其内部表示

    • 阅读小端法机器的程序表示时。

    • 规避正常类型系统时,即运用了强制类型转换或联合。例如:运用强制类型转换生成对象的字节表示。

      #include
      
      typedef unsigned char *byte_pointer; //指向字节序列的指针
      
      void show_bytes(byte_pointer start, size_t len){
          size_t i; //size_t是表示数据结构大小的首选数据类型(这里为sizeof的结果)
          for(i = 0; i < len; i++) printf("%.2x", start[i]); //%.2x表示两位16进制数
          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 *));
      }
      //这里的强制类型转换并未改变指针, 只是告诉编译器以新的数据类型看待被指向的数据
      //例如, show_int中将&x看作int指针时, 进入"下一个"地址会跨越四个字节; 看作char指针时, 进入"下一个"地址只会跨越1个字节
      

表示字符串

  • 字符串编码以null(0x00)结尾。
  • 最常见的字符编码是ASCII码。字符串的字节序列在使用ASCII编码的任何系统上都有相同结果,与字节顺序规则和字长大小无关。

表示代码

  • 从机器的角度看,程序代码只是字节序列。
  • 不同机器对于相同程序的二进制编码不同且不兼容。

布尔代数

  • 位向量可用于表示有限集合的子集,位向量的与或非等价于集合的交并补。
  • 布尔代数与整数算术有相似之处:交换律,结合律,分配律(布尔代数的分配律可以理解为对整数算术分配律的推广。因为布尔代数中,不仅&对|有分配律,|对&也有分配律)

C语言中相关运算

位级运算:&,|,~
  • 掩码运算掩码表示从一个字中选出的位的集合,如0xFF表示一个字低位字节都是1。x&0xFF表示x的最低字节不变,其他字节都被置为0。

    习题2.10&2.11

    注意:指向相同地址的两个指针,改变其中一个,则另一个也会变

    #include
    #include
    
    void inplace_swap(int *x, int *y){
        *y = *x ^ *y;
        *x = *x ^ *y;
        *y = *x ^ *y;
    }
    
    void reverse_array(int a[], int cnt){
        int first, last;
        for(first = 0, last = cnt - 1; first <= last; first++, last--)
            inplace_swap(&a[first], &a[last]);
    }
    
    int main(){
        int a[5] = {1, 2, 3, 4, 5};
        reverse_array(a, 5);
        for(int i = 0; i < 5; i++) printf("%d", a[i]);
        //最终结果为{5,4,0,2,1}, first = last时, *x = *y, *y = *x ^ *y = 0时, *x也被置为0
        return 0;
    }
    
逻辑运算:&&,||,!
  • 逻辑运算中,只有0(FALSE)和1(TRUE),所有非零参数均表示1(TRUE)
  • 逻辑运算具有提前终止的特点。当第一个参数就能确定表达式的值时,就不会计算第二个参数。
移位运算:<<,>>
  • 左移即在低位补0。右移分为逻辑右移算术右移。逻辑右移在高位补0,算术右移在高位补符号位。
  • 由w位组成的数据,移动k位。若k>w,则只视为移动k mod w位。移位指令只考虑移位量k的低logw位。
  • 移位运算符合左结合律:x<
  • 移位运算优先级低于加减法:x<

整数表示

整形数据表示

  • unsigned / (signed) + char, short, int, long
  • 数据类型取围:32位、64位、C语言标准均有不同

编码

无符号数(Unsigned)
  • B2U
  • UMax, UMin
  • 无符号数编码具有唯一性(B2U是双射)
补码(Two's-Complement)
  • B2T

  • TMax, TMin

    -TMax = 0 - TMax = TMax

  • 补码编码具有唯一性(B2T是双射)

  • |TMin| = TMax + 1

  • UMax = 2 * TMax + 1

  • -1和UMax有相同的位模式

  • 有符号数的其他表示方法

    • 原码(Sign-Magnitude)
    • 反码(Ones'-Complement):对于无符号数x,-x的w位表示在反码中相当于[11…1] - x,在补码中相当于2^w - x
无符号数和有符号数之间的转换
  • 保持位模式不变

  • 对于x,0≤x≤TMax时T2U(x) = U2T(x),否则转换时需对x加上或减去2^w

2-2.png
C语言中无符号数与有符号数的转换
  • 显示转换:强制类型转换
  • 隐式转换
    • 不同类型变量赋值:保持被赋值的变量类型不变
    • 输出函数printf中通过"%d"与"%u"进行转换
    • 运算中,如果有符号数和无符号数同时出现,则视为无符号运算

扩展位表示

  • 无符号数进行零扩展,值不变。
  • 有符号数进行符号扩展,值不变。
    • 高位全是1和只有最高位为1对数值大小没有影响,可用于简化运算。

截断数字

  • 直接截断高位。
  • 无符号数相当于取模(x%2^w = x')
  • 有符号数相当于转化为无符号数取模后,再转化为有符号数。

有符号数和无符号数之间的转换容易引起错误。详见习题。

整数运算

模数运算形成数学结构:阿贝尔群

无符号数运算和补码运算有完全相同的位级表示。

加法

无符号加法

正常情况x+y的值保持不变,溢出情况的结果是x+y-2^w

2-3.png

检测无符号加法的溢出:对于无符号数s=x+y,当且仅当s

补码加法

正常情况x+y的值不变,正溢出情况结果是,负溢出情况结果是。

2-4.png

检测补码加法的溢出:对于补码s=x+y,当且仅当x<0,y<0,s≥0时,发生负溢出;当且仅当x>0,y>0,s≤0时,发生正溢出。

取非

得到取非结果的位级表示的方法:①按位取反后加一。②设k是最右边的1的位置,对k左边所有位取反即可。

无符号数

对于无符号数-x,x=0时结果是其本身,否则结果是2^w-x

补码

对于补码-x,x=TMin时结果是其本身,否则结果是-x

乘法

无符号数

x * y=(x * y) mod 2^w

补码

将结果位级表示转化为补码即可

乘以常数

与2的幂相乘

与2的k次幂相乘转化为左移k位实现。

与任意常数相乘

将常数K表示为0和1交替的二进制序列,将乘法运算转化为移位运算和加减法的组合。

设K的二进制表示中所有的1连续出现,且从位置n到位置m有连续的1

形式1:x * K = (x<

形式2:x * K = (x<<(n+1)) - (x<

不符合条件的情况可通过变换得到。

除以2的幂

无符号数

除以2的k次幂转化为右移k位实现,得到向下取整的结果。

补码

正数向下取整:除以2的k次幂转化为右移k位实现

负数向上取整:除以2的k次幂转化为,先加偏置量2^k-1=(1<

  • 原理:

“溢出”是针对码值转化为数值后,不符合直观运算规则而言的。但实际上码值运算规则无谓“溢出”,永远符合相同规则。如A+B=C就有C-B=A。

浮点数

二进制小数

IEEE浮点表示

  • 符号s:0正1负

  • 尾数M:1 ~ 2-ε 或 0 ~ 1-ε

  • 阶码E

2-5.png
  • 符号位s编码符号s

  • k位阶码字段exp编码E

  • n位小数字段frac编码M

2-6.png
  • IEEE编码保证了从非规格化到规格化的平滑过渡
规格化

E=Exp-Bias

  • Exp是exp的无符号整数值
  • Bias=
  • exp≠00…0且exp≠11…1
  • M=1.frac
非规格化

E=1-Bias

  • Bias=2^k-1
  • exp=00…0
  • M=0.frac
  • 用于表示0附近的小数值
特殊值
无穷大

exp=11...1,frac=00...0

NaN(Not-a-Number)

exp=11...1,frac≠00...0

舍入规则

  • 向偶数舍入:四舍六入五凑偶
  • 改变编码格式时,对于非规格化表示,先考虑能否转化为规格化保留精确度,否则再舍入
  • 其他舍入方式:向0舍入,向下舍入,向上舍入

浮点运算

加法
  • 满足交换律,不满足结合律
  • 除了无穷和NaN之外都有逆元
  • 符合单调性:若a≥b,则x+a≥x+b(a,b,x为除NaN之外的任意值)。无符号数和补码加法不符合单调性
乘法
  • 满足交换律,不满足结合律
  • 乘法对加法不满足分配律
  • 符合单调性:a≥b,若c≥0,则a * c ≥ b * c;若c≤0,则a * c ≤ b * c(a,b,c为除NaN之外的值)无符号数和补码乘法不符合单调性
  • a * a ≥ 0(a≠NaN)

类型转换

  • int -> float 不会溢出,但可能舍入
  • int/float -> double 不会溢出,保留精度
  • double -> float 可能溢出或者舍入
  • float/double -> int 向0舍入,或在无法确定整数近似值时(例如溢出)产生TMin

  1. 对于程序存储的管理完全是在虚拟地址空间中进行的。例如,指针指向的就是字节块的虚拟地址。 ↩

你可能感兴趣的:(第二章 信息的表示和处理)