第二章学习起来还是并不简单,书上公式和推导很多,所以在数学上感觉有些考验敏感度
1.进制
给出一个表。
一个字节由8位组成。每台计算机都有一个字长,指明指针类型数据的标称大小,指针类型能表示多大的地址值决定了虚拟地址空间的最大大小,所以对于一个字长为32位的机器,虚拟地址范围为0~(2^32)-1,空间为4GB。对于64位机器,空间可达16EB。
|C声明 | 字节数(32) | 字节数(64)|
|–|–|–|–|–|
|char | 1 |1 |
|short | 2 | 2|
| int |4 | 4 |
|long |4 |8 |
|float | 4 | 4|
| double | 8 |8 |
| char *| 4 | 8|
2.数据在内存中的形式
排列表示一个对象的字节有两个通用的规则,即大端法和小端法。
小端法则是最低有效字节在最前面,大端法最高有效字节在最前面
大多数IBM兼容机都只用小端模式
可用如下代码查看数据在内存中的表示
#include <stdio.h>
typedef unsigned char *byte_pointer;
void show_bytes(pointer start, size_t len) {
size_t i;
for (i = 0; i < len; i++)
printf("%p\t0x%.2x\n", start + i, 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 *));
}
布尔运算:
与 And:A=1 且 B=1 时,A&B = 1
或 Or:A=1 或 B=1 时,A|B = 1
非 Not:A=1 时,~A=0;A=0 时,~A=1
异或 Exclusive-Or(Xor):A=1 或 B=1 时,A^B = 1;A=1 且 B=1 时,A^B = 0
对应与集合运算则是交集、并集、差集和补集,假设集合 A 是 {0, 3, 5, 6},集合 B 是 {0, 2, 4, 6},全集为 {0, 1, 2, 3, 4, 5, 6, 7 } 那么:
& 交集 Intersection {0, 6}
| 并集 Union {0, 2, 3, 4, 5, 6}
^ 差集 Symmetric difference {2, 3, 4, 5}
~ 补集 Complement {1, 3, 5, 7}
3.整型
分为有符号数和无符号数:
简单地说二进制转无符号数(B2U)就是这个二进制本身所表示的,而二进制转有符号数(B2T)也称为补码表示,最高位是符号位。正数无符号数和有符号数表示相同。
极值:字长为w
字长为16的情况下:
类型转换
无符号数和有符号数的转换(U2T\T2U)
当一个w位的位向量向量表示无符号数(U)和相同的位向量表示补码(T)在十进制下发现有以下关系:
U-T=2^w
在C语言中,默认的整型是有符号的,如果想要无符号数可以在数字后加U
如:
int a=123;
unsigned int b=321U;
如果一个表达式既含有无符号数和有符号数,将会被隐式转换为无符号数计算
当较小的类型转换到较大类型:
有符号:补符号位
无符号:补0
当较大类型转换到较小类型:
无符号:x mod 2^k(截断到k位)
有符号:先转到无符号如上操作再转到补码
运算与溢出:
对于无符号数加法,如果两个 w 位的数字相加,结果是 w+1 位的话,那么就会丢弃掉最高位,实际上是做了一个 mod 操作(公式为 s=UAdd(u,v)=u+v mod 2^w)
对于有符号的加法,操作过程和无符号加法一样,只是解释的时候会有不同,因此会得到正溢出和负溢出两种。正溢出就是数值太大把原来为 0 的符号位修改成了 1,反而成了负数;负溢出是数值太小,把原来为 1 的符号位修改成了 0,反而成了正数。
对于乘法来说,值的范围会大很多,这里分情况讨论一下,假设两个乘数是 x,y 并且都是 w 位的
计算的无符号乘法的时候,会忽略最高的 w 位,相当于 UMult(u,v)=u⋅v mod 2^w
乘以常数:
-
与2的幂相乘的无符号乘法和有符号乘法一样,
x*2^k
在C表达式中为:x<<k
-
乘法指令相当慢,许多C语言编译器试图用移位和加减运算加快运算:
形式A(一般更快)·x*6==(x<<3)-(x<<1) \\x*(8-2)
形式B :
x*6==(x<<2)+x(x<<1) \\x*4+x*2
除以2的幂:
整数除法总是舍入到0,向下舍入一个正值,向上舍入一个负值。对于舍入:3.14 ≈4 (向上舍入),3.14≈3(向下舍入),-3.14 ≈-3(向上舍入)-3.14 ≈-4(向下舍入)
无符号逻辑右移,有符号算术右移
- 无符号数:x/2^k ≈ x>>k(向下舍入)
- 有符号数:x/2^k ≈ x>>k(向下舍入)(正值)
x/2^k ≈ (x+(1<<k)-1)>>k (向上舍入) (负值)
解释:在移位之前偏置了这个值,来修正舍入.(书中P73)
合为一个表达式为:(x<0?x+(1+ k) - 1: x)>>k
4.浮点数
二进制小数
IEEE浮点数表示
- 符号(sign) s决定正负性, 对于0的符号位解释做特殊处理
- 阶码(exponent) E的作用是对浮点数加权,权重是2的E次幂
- 尾数(significand) M是一个二进制小数,它的范围是 1−2-ϵ
浮点数的位表示被划分为三个字段,分别对这些值进行编码:
- 一个单独的符号位直接编码成符号s
- k位的阶码字段exp 编码成阶码E
- n位小数字段frac 编码成尾数M
此处引入书中表格,感受一下
试着将一个整数值转换成IEEE浮点表示的对理解浮点表示很有用:
- 将整数化为二进制表示
如:12345=[11000000111001] 转成float - 构造尾数
将二进制小数点左移十三位 [11000000111001] =1.1000000111001 * 2^13
取小数点后[1000000111001]补0到23位即 [10000001110010000000000]构成尾数M - 构造阶码
求偏置量(Bias), 在float中,exp有8位(k=8),E=exp-8, 由上可知E=13,Bias=2^8-1=127,所以exp为13+127=140,二进制表示为[10001100], - 添符号
12345为正,符号位为0
综上12345.0 的float位表示为[0][10001100][10000001110010000000000]
舍入
对于浮点数的加法和乘法来说,我们可以先计算出准确值,然后转换到合适的精度。在这个过程中,既可能会溢出,也可能需要舍入来满足 frac 的精度。
向偶数舍入是默认方式
浮点数加法
浮点数乘法
C语言中的浮点数强制转换
- 当 int 转换成 float,不会溢出但可能被舍入
- 从int 或 float 转换成 double,可以保留精确的数值
- 从double 转换成 float,值可能溢出成无穷,还可能被舍入
- 从float 或者 double 转换成 int 值将会向零舍入