在内存中按照从最低到最高有效字节的顺序存储对象,这种最低有效字节在最前面的方式,称为小端法。
在内存中按照从最高到最低有效字节的顺序存储对象,这种最高有效字节在最前面的方式,称为大端法。
例如:对于int32类型变量,其存储地址位于0x100,其十六进制值为0x12345678
,其地址范围为ox100~0x103
:
// 大端法
地址:0x100 0x101 0x102 0x103
12 34 56 78
// 小端法
地址:0x100 0x101 0x102 0x103
78 56 34 12
大端法表示与正常书写时的字节顺序一致。
#include
typedef unsigned char* byte_pointer;
void show_bytes(byte_pointer start, int len) {
int i;
for (i = 0; i < len; i++) {
printf("%02x ", 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*));
}
在使用ASCII码作为字符码的任何系统上都将得到相同的结果,与字节顺序和字节大小规则无关。因而,文本数据比二进制数据具有更强的平台独立性。
位移表示对一个二进制位向左位移和向右位移。
向左位移:左移k位,并丢弃最高的k位,并在最后补0。
**向右位移:**向右位移分为逻辑右移和算术右移。
// 算术右移 0x8A -118 >> 3 = -15
1000 1010 // -118
// 右移3位,由于是负数,因此在最左边3位补 1
1111 0001 // -15
整数取值,负数的范围比正数大1。一般定义:
#define INT_MAX 0x7fffff
#define INT_MIN -INT_MAX - 1
无符号数编码:
1001: 1 * 2^3 + 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = 9
1010: 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 0 * 2^0 = 10
1111: 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 1 * 2^0 = 15
补码编码:最常见的有符号数的计算机表示方式就是补码。补码中,将最高有效位解释为负权。
1001:1 * 2^(-3) + 0 * 2^2 + 0 * 2^1 + 1 * 2^0 = -7
1010: 1 * 2^(-3) + 1 * 0^2 + 1 * 2^1 + 0 * 2^0 = -6
1111: 1 * 2^(-3) + 1 * 2^2 + 1 * 2^1 + 0 * 2^0 = -1
有符号数和无符号数之间的转换:强制类型转换的结果保持位值不变,只是改变了解释这些位的解释方式。
w
位的补码数字, 当 x >= 0
时,直接转换;当x < 0
时为-2^w + x
;w
位的补码数字, 当x <= 2^(w-1) - 1
时,直接转换;当x > 2^(w - 1) - 1
时,为x - 2^w
short int v = -12345;
// 1100 1111 1100 0111 ->
unsigned short uv = (unsigned short) v;
printf("v = %d, uv = %u\n", v, uv); // v = -12345, uv = 53191
对于C语言,如果一个数是有符号,另一个是无符号,那么C语言会隐式地将有符号参数强制转换为无符号数,并假设这两个数都是非负数。
// INT_MAX = 2147483647 INT_MIN -2147483648
int b1 = -1 < 0; // 1
int b2 = -1 < 0; // 1
int b3 = 2147483647 > -2147483647 - 1; // 1
int b4 = 2147483647U > -2147483647 - 1; // 0
int b5 = 2147483647 > -2147483647 - 1U; // 0
扩展一个数字的位表示:
截断数字:
x mod 2^k
-2^w
无符号加法:对满足 0 <= x, y <= 2^w
当 x + y < 2^w
时,正常输出,为x + y
当-2^w <= x + y < 2^(w + 1)
时,发生溢出,此时结果为x + y - 2^w
判断无符号加法是否溢出:x + y >= x
表示没有溢出,否则溢出了
无符号数取反:对满足 0 <= x < 2^w
x = 0
时,返回x
x > 0
时,返回2^w - x
补码加法:对满足-2^(w-1) < x, y < 2^(w-1) - 1
x + y >= 2^(w-1)
时,结果减去2^w
,表示为x + y - 2^w
-2^(w-1) <= x + y < 2^(w-1)
,正常输出为x + y
x + y < -2^(w-1)
时,结果加上2^w
,表示为x + y + 2^w
补码取反:对满足-2^(w-1) < x < 2^(w-1) - 1
当x = TMin
时,返回TMin
当x > TMin
时,返回 -x
计算:-x = ~x + 1
// 例如 5 -> -5,0101 -> 1011
5 -> 0101
~5 -> 1010 = -6-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
~5 + 1 -> 1010 = -5
无符号乘法:对满足 0 <= x < 2^w
,其结果为(x.y)mod2^w
,在大多数机器上,乘法指令很慢,需要10个或更多时钟周期。
补码乘法:对满足-2^(w-1) < x, y < 2^(w-1) - 1
除以2的幂:整数除法比乘法更慢,需要30个或更多时钟周期。除以2的幂可以用右移来实现。
2^k
再舍入到零(舍去小数部分)是相同的结果。(x + (1<> k
二进制浮点数表示:101.11 = 1 * 2^2 + 1 * 2^0 + 1 * 2^(-1) + 1 * 2^(-2) = 4 + 1 + 1/2 + 1/4 = 23/4
.
小数的二进制表示法只能表示那些能够被写为x + 2^y
的数
IEEE浮点数表示:V=(-1)^s * M * 2^E
。浮点数的三个基本组成部分:符号位、阶码位(或指数位)和尾数位(或小数位):
s
为示符号位。这是一个二进制位,用于表示浮点数的正负。0代表正数,1代表负数。E
为阶码。用于表示浮点数的指数,即小数点相对于尾数部分移动的位置。在IEEE 754中,阶码通常采用偏移量的表示方式,这样可以表示更大范围的指数。对于单精度浮点数(32位),阶码占8位,实际存储的是e-Bias
,其中e是实际指数,Bias是一个预设的偏移量(对于单精度,Bias是127;对于双精度,Bias是1023)。M
为尾数。也称为小数位,记录了浮点数的有效数字。在IEEE 754中,尾数通常是一个二进制小数,而且为了节省空间,会省略掉最高位的1(因为对于正常的非零数,这个位置总是1),所以实际存储的是小数点后的部分。IEEE 标准还定义了一些特殊值,用于表示特定的数学情况或错误:
1-Bias
。主要有两个用途:
+0.0
或-0.0
。0.0
的数,它们提供了一种属性,称为下溢。/* 例如:8位的浮点数,阶码位为4,小数位为3。Bais = 2^4 - 1 = 7*/
/* 非规格化数值:阶码计算为 1 - Bais = -6 */
0 0000 000
/* 表示+0.0 */
0 0000 001
/* 最小的非规格化数:
* 权:E = 1 - 7 = -6,权 2^E = 1/64
* 小数部分 f = 0 * 2^(-1) + 0 * 2^(-2) + 1 * 2^(-3) = 1/8
* 值: 1/64 * 1/ 8 = 1/512
*/
0 000 1119-+8*/7····z
468*8*8*8*8*8*
/* 最大的非规格化数:
* 权:E = 1 - 7 = -6,权 2^E = 1/64
* 小数 f = 1 * 2^(-1) + 1 * 2^(-2) + 1 * 2^(-3) = 7/8
* 值:1/64 * 7/8 = 7/512
*/
/* 规格化值:阶码计算为 2^e - Bais */
0 0001 000
/* 最小的规格化值
* 权:E = 1 - 7 = -6,权 2^E = 1/64
* 小数:1 + 0
* 值:1/64 * 1
*/
0 0111 000
/* 1
* 权:7 - 7 = 0 权:1
* 小数:1 + 0
* 值:1 * 1
*/
0 1110 111
/* 最大的规格化数
* 权:E = 14 - 7 = 7 权:2^E = 128
* 小数:1 + 7/8 = 15/8
* 值:128 * 15/8 = 240.0
*/
/* 特殊值 */
0 1111 000 /* +0.0 */
1 1111 000 /* -0.0 */
IEEE 标准定义了两种主要的浮点数类型:单精度和双精度:
舍入:对于值x
,能够找到最接近的匹配值,它可以用期望的浮点数表示出来,这就是舍入运算的任务。IEE有四种舍入方式:
向偶数舍入:也被称为向最近的值舍入,是默认的方式。其规则如下:
2.4 -> 2
2.6 -> 3
2.5 -> 2 /* 小数部分是 0.5,最后一位是偶数,则舍去小数,返回 2 */
1.5 -> 2 /* 小数部分是 0.5,最后一位是奇数,则向上舍入,返回 2 */
向零舍入:直接截尾,不考虑小数部分的值,即向数轴零点方向舍入。x
向零舍入为x0
,那么|x0| < |x|
;
向上舍入:把正数和负数都向下舍入。x
向上舍入得到 x+
,那么x <= x+
向下舍入:把正数和负数都向下舍入。x
向下舍入得到x-
,那么x- <= x