《斯坦福大学:编程范式》第二节:基本数据类型在内存中的二进制表达

我们以C/C++为例。

基本数据类型有:

bool        1 byte

char  1 byte       256个字符,其中75个字符是常用的    

short  2bytes

int   4bytes

long  4bytes

float  4 bytes

double 8 bytes

---------------------------bytes-------------------------------------

1 bytes(字节) = 8 bit(位 binary digit)

1 bit 只能表示两个数字 0和1 ,代表高电压 低电压。

 1 byte  可以表示2的8次方 =256个字符。

 

----------------------------------char------------------------------------------------

 

'A' = 65 = 2的6次方+ 2的0次方。  内存中 表示为  0100,0001   

 

--------------------------------------short--------------------------------------------

2的16次方数量的数字 :0 到 (2的16次方-1)

比如 1000,0000,0000,0001  =  2的15次方+2的0次方

 

-------------------------------------内存中二进制的加----------------------------------------------------

     1001 1001

  + 0101 0001

 =  1110 1010

从低位往高位加即可。

 -------------------------------------内存中二进制的减----------------------------------------------------   

     1001 1001

  -  0101 0001

 =  0100 1000

从低位往高位减即可。

 -------------------------------------以上都说的正数,如何表示负数?-------------------------------------

对于一个二进制数,比如 0000,0011

最高位的0 表示它是 正数3

 

比如 一个short数, 1011

最高位的1 表示它是 负数

但是,这个负数,不代表 -3

 因为二进制的负数,是用补码表示的。

 

 ---------------------为什么要用最高位表示负数?——————————————

因为 我们希望二进制的加法能够同时兼容正数和负数

比如 正数7  是 0000,0111

         负数7 是 1000,0111

   我们希望 他们相加,能很优雅的变成0.

 如果按之前的加法运算,则

  0000,0111

+1000,0111

= 1000,1000 

 为 -14,显然不是我们期望的。

 

一个二进制怎么方便的变成 0 呢? 

 

也就是 0000,0000

 

我们发现    

  1111,1111 

+0000,0001 

= 1,0000,0000

最高位的1,因为没有内存可以存放,溢出了,所以会被抛弃掉,

所以我们得到 0000,0000

 

所以,我们可以先让一个二进制变成 1111,1111 这种模式。

 

对于之前的 

 0000,0111

+ 1111,1000

=1111,1111

很明显,对一个二进制取反,再相加,就得到 所有位为1.

1111,1111

 

+0000,0001

= 0000,0000

 

所以我们发现:

0000,0111

 

+1111,1001

= 0

 

同样,一个负数,要得到它的正数

比如 1111,1001

取反:0000,0110

加1: 0000,0111

 

 

所以一个二进制的负数,是 他的取反+1

-x=!x+1 

-----------------------以及如何处理有负数的加法———或者说,如何表示正数之间的减法?———

 

比如 正数7  是 0000,0111   

         负数7 是 1000,0111

计算过程为 7 -7 =0;

在二进制里: 我们只有加法,转换为 7

 

 

 

我们对负数7 取反 得 0111,1001

  0000,0111

 

+ 0111,1001

=    1000,0000

 

-----------------------数据基础类型之间的转换———

 

本质是位模式的拷贝

-----------------------char转short———

char ch = 'A';

short  a = ch;

cout<< a<< endl;

结果是 65。

因为 char 中 ‘A’  在二进制中是 0100,0001

赋值给short, 则为16位了 :  0000,0000,0100,0001

等于65

-----------------------short转char———

 

short s = 67;

char ch  =s;

cout<<ch <<endl;

输出为 'C' 

 

-----------------------int转short———

长于 16位的  被截断。(第16位是符号位)

int a = 0001,0000,0000,1110, 0100,0000,0000,0001;

short b = a;

那么b =  0100,0000,0000,0001;

 

-----------------------有符号的short———

short s = -1;

int i  = s;

 s =   1111,1111,1111,1111,     1111,1111,1111,1111, 

 i =   1111,1111,1111,1111,     1111,1111,1111,1111,   1111,1111,1111,1111,     1111,1111,1111,1111, 

对于有符号的二进制的赋值,

多余的位数,会用符号位 全部赋值, 这叫 “符号扩展”。 为了保证 之前的 +1 形成的 多米罗骨牌 能继续生效。

 

 

-----------------------浮点数的表示 float 和double———----------------------------------------------------------------------------------------

float a =   (前面位数都为0) 0000,1111

假设 我们的表示方式 跟上面一样 

为了表示小数, 我们假设 前面用来表示整数部分,后面8位表示小数部分。

 第一个1 为 2的-1次幂,  第二个1 为2的 -2次幂, 第3个1 为2的 -3次幂, 第4个1 为2的 -4次幂, 

那么 float a =  0.5 +0.25+ 0.125+ 0.0625

 

我们发现 按这种思路,无法表达很大和很小的浮点数,而且数字非常不连续。

 

真正的浮点数表示是?

 

一个规格化的32位浮点数x的真值为:

x=(1)s×(1.M)×2E127

一个规格化的64位浮点数x的真值为:

x=(1)s×(1.M)×2E1023

 

 

这里的E 是指 exp(指数部分) 最大为255,最小为0

M 指 尾数部分 ,最大为全部位为1,最小为全部位为0

S 指 符号位, 符号位 = 0, 则 -1 的S次幂 = 1;  最终为正数。

 

 

 

-----------------------整数 转 浮点数———----------------------------------------------------------------------------------------

int a = 5;

float b = a;

cout<<b<<endl;

打印为 5,但是在二进制里,这次不是位拷贝了,是如上面例子,转换为 浮点数的表达方式。

 

浮点数转 整数 同理。 只是截取了整数部分。

——————————————一些疯狂的例子————————————————————————

int i = 37;

float f =  * (float *)&i ; 

打印 f, 此次 f  != 37 了。

 

这里其实是对 i 的地址求值。

 

从右往左运算

1. 我们首先 &i    拿到 指向 i的指针, 因为i 是一个int型,所以 &i  是 int* 类型,代表了一个变量的地址,这四个直接存放了

2. 然后我们诱导它变成了 float* 类型  ,

注意

int b;

 

 float a =  b  

是强行转换类型 作为float 存储在内存中

而  (float) b    是在cpu计算过程中 诱导 把它认为float, 两者有区别),

这并没有让 各个bit 发生变化, 所以“我只能假装我是其他类型了” 。

3. 然后 对于类型系统来说,  会尝试 解引用为float

  所以 37  落在低位的位置,也就是 float的尾数部分。然后左边所有位都是 0 。 所以是一个很小的正的浮点数

 

 -------------------------------------

float f = 7.0f;;

short s = *(short *) &f;

 

1.    float 是32位, short 是16位。

 &f  拿到 f的地址。

2. (short *)  代表 这个地址被 当做一个指向short的指针。

3. 所以,我们只会去拿 short这么长的位数,16位,尝试解析成short。 

那么从高位拿起,还是低位呢?  从高位拿起!

因为指针是指向 右边高位的第一个bit !

这个跟 位拷贝不同, 位拷贝,是从低位拷贝起。

所以我们拿到 float的高位的16位。 最后当做short解析。

当然,不再是7了。

 

posted on 2018-06-14 01:37  百无禁忌  阅读(301)  评论(0编辑  收藏  举报

导航