一个小问题引出的
在看C专家编程,遇到一段代码:
#include <stdio.h> int array[] = {1,2,3,4,5,6,6}; #define TOTAL_ELEMENTS (sizeof(array))/sizeof(array[0]) int main() { int d = -3; int x = -3; if(d <= TOTAL_ELEMENTS - 2) { x = array[d + 3]; } return 0; }
这里的本意显然是要给x赋值,但实际上却不会进到这里,这里引出了2个问题:
1.TOTAL_ELEMENTS的类型
sizeof返回值的类型为size_t,而typedef unsigned int size_t,所以TOTAL_ELEMENTS - 2的类型为unsigned int.
这里引出第二个问题
2.类型转换
d的类型为int,这里的比较就涉及到类型转换了,一般都是低级向高级转换,而低级向高级转换的序列大致为
short,char ---> int -----> unsigned int ---> float ---> double <--- long
所以这里d会提升数据类型,即-3转换成(unsigned)-3,其实这里的-3在字节层面并未改变,只是从之前的有符合类型,现在解释为无符号类型,
-3的二进制表示为 -3 = -1 - (2) = 0xffff ffff -(2) = 0xffff fffd
现在解释为无符号数自然是个很大的数,所以不会进行赋值操作了。
到这里自然而然会想到数据的编码问题
一:整数的编码
1.无符号编码
这个很简单,比如10 = 1010,扩展为32位就是0000 0000 0000 1010.
2.有符合编码
这个比较麻烦了,比如-10,这个怎么表示呢?
先给出一个比较快捷的方式,那就是-10 = 1- - (9)。然后问题就转换为-1的二进制表示究竟是怎样的?
先给出如下一个事实:
-1 = 0x11
-1 = 0x111
-1 = 0x1111
-1 = 0x11111
...
-1 = 0xffff ffff
也就是说不管机器是多少位,0x111..1都是表示-1.
在这个基础之上,负数的二进制表示就方便了,一律通过-1来求,比如:
-10 = -1 - 9 = 0xffff ffff - 9 = 0xffff fff6
如果你不太放心,再提供另外一个方法进行检验结果:
0xffff fff6 = 0x1111 1111 1111 1111 1111 1111 1111 0110 = -1 * 2 exp (31) + 1 * 2 exp(30) + ... + 1 * 2 exp(2) + 1 * 2 exp(1) + 0 * 2 exp(0) = -10
如上,每个位上都有对应的权重,即使带小数点,这个权重也是适用的,只不过权重变为exp(-1),exp(-2)...
二.浮点数(实数、带小数点)编码
浮点数比较复杂,主要通过举例来讲解,首先给出32体系下浮点数的分布情况
例一:
12.25
第一步:这是一个正数,所以31位是0
第二步:转换成二进制表示,1100.01
第三步:转换成科学表示法,必须以1.开头,即1.10001 * 2 exp(3)
第四步:计算指数部分,3 + 127(这地方的127表示指数偏移基数,32位的指数基数为127,64位为1023,这个也不用纠结) = 130 = 1000 0010
第五步:计算小数点后面的,貌似也叫尾数,同样的叫啥不重要,10001,不足23位的右补0
所以最后在机器中的二进制表示为0 1000 0010 10001 000000000000000000
十六进制表示为0x4144 0000
给出验证代码:
#include <stdio.h> int main() { float a = 12.25; int* p = (int*)&a; printf("%#x\n", *p); return 0; }
例二:
-12.25
与例一唯一区别就是31位的符号为1,所以结果为0xc144 0000
例三:
10.3
这个与上面的是会有区别的地方是转换成二进制的时候,是无穷的,
第一步:这是一个正数,所以31位是0
第二步:转换成二进制表示,主要是0.3的二进制表示,
0.3 0.6 0.2 0.4 0.8 0.6 ...
0 1 0 0 1 ...
直到填满23位,最终表示为1010.010011 0011 0011 0011 0011 0
第三步:转换成科学表示法,必须以1.开头,即1.010010011 0011 0011 0011 0011 0 * 2 exp(3)
第四步:计算指数部分,3 + 127 = 130 = 1000 0010
第五步:计算小数点后面的,010010011 0011 0011 0011 00 11 0
这里26位了怎么处理呢?难道要进位,从而是010010011 0011 0011 0011 01??试试呢,这样的话
最后在机器中的二进制表示为0 1000 0010 010010011 0011 0011 0011 01
十六进制表示为0x4124cccd,用上面的程序验证,竟然对了!!!