信息的表示和处理

信息存储

大多数计算机用8位的块,或者字节,作为最小的可寻址的存储器单位,为不实在存储器中单独访问的位

整数的表示

有符号:我们默认使用的都是有符号的(有正负),例如:int i = -1 无符号:没有负数,只有正数,例如:unsigned i = 1

有符号的最高位表示正负(1表示负数,0表示正数),其余的就正常运算。由于有符号比无符号的多了一位表示位数的,所以,最大值是无符号的一半减1,最小值是无符号的一半

原码:原始的二进制(最高位为符号位) 反码:正数的的反码是其本身,负数除最高位外,其余位取反 补码:正数的本身就是其补码,负数的补码就是符号为不变,其余位取反,最后+1(其实就是取反码然后+1) 注意:有符号数-1的二进制表示为:1000 0001 但是在计算机中,我们看到的二进制却是1111 1111,这是因为计算机是以补码的形式来表示的

数字,字母其实就是按照其2进制的表示,直接映射在对应的虚拟内存地址中,所以我们平时打印某个数字的二进制的时候,不论在什么环境下地址都是一样的

有符号强转为无符号:直接按二进制的值来读取就可以了 无符号强转为有符号:因为有符号的在计算机上表示的都是补码,所以,我们解析出补码的原码,然后按有符号的二进制读取方式即可(最高位表示正负,其余正常的二进制计算)

有符号的int的范围,最小值为何比最大值多了1个-128,127:主要是因为:有符号的1000 0000 与 0000 0000 分辨表示了 +-0,这并没有什么意义,所以,就将-0表示成-128.(即:1000 0000 表示为-128 ). 最大补码表示为:0111 1111,最小补码表示为:1000 0000

计算机如何进行加减法运算:二进制的加减法都是使用补码来进行计算的(因为:原码不能计算减法,反码又存在+-0的问题,所以就有了补码(就是反码+1))

1
2
3
4
5
6
7
8
9
10
11
12
13
//1-1=0   1+(-1)=0  (十进制)
[0000 0001](原码) + [1000 0001](原码) = [0000 0001](补码) + [1111 1111](补码) = [1 0000 0000](溢出了,最高位就抛弃了) = [0000 0000](补码) = [0000 0000](反码) = [0000 0000](原码)  = 0(十进制)

//1-2=-1   1+(-2)=-1  (十进制)
[0000 0001](原码) + [1000 0010](原码) = [0000 0001](补码) + [1111 1110](补码) = [1111 1111](补码) = [1111 1110](补码-1=反码) = [1000 0001](原码)) = -1(十进制)

//1+2=3   (十进制)
[0000 0001](原码) + [0000 0010](原码) = [0000 0001](补码) + [0000 0010](补码) = [0000 0011](补码) = [0000 0011](反码) = [0000 0011](原码)= 3(十进制)


//2-1=1  2+(-1)=1 (十进制)
[0000 0010](原码) + [1000 0001](原码) = [0000 0010](补码) + [1111 1111](补码) = [1 0000 0001](溢出了,最高位就抛弃了)[补码] = [0000 0001](补码) = [0000 0001](反码) = [0000 0001](原码) = 1(十进制)

参考

原码, 反码, 补码 详解

C语言有符号数与无符号数之间的转换

为什么8位有符号类型的数值范围是-128~127

深入理解计算机系统(2.4)——整数的表示(无符号编码和补码编码)

关于有符号数和无符号数的转换

原码, 反码, 补码 详解

整数运算

加法:补码相加 减法:转换为加法,也是补码相加 乘法:编译器会现将乘法重写,然后通过左位移+加法来实现,例如:

1
2
3
4
5
//X*14
14 = 2的3次方+2的2次方+1的一次方
这样编译器就可以将乘法重写为
X<<3 + x<<2 + x<<1
这样,位移后,再进行加法即可

逻辑移位:不管是左移位还是右移位,都是空缺处补0
算数移位:要保证符号位的不改变(逻辑左移位补0,逻辑右移位看符号位,符号位是1则补1,符号位是0则补0)算数右移,如果是负数填充1的原因是,计算机表示的是反码,1真正的二进制其实是0

除法:类似乘法,只是将左位移改成了右位移(对于除以 2 的幂可以用移位来运算。无符号除法使用逻辑移位,补码除法使用算术移位。),例如:

1
2
3
4
5
6
7
8
9
10
11
无符号:16/8=2
2^3=8 //所以直接右移3位即可
[0001 0000]>>3 = [0000 0010] = 2

有符号:-16/8 = -2
2^3=8
[1111 0000](补码)=> 算数移位>>>3  = [1111 1110](补码) => [1111 1101](反码) =>[1000 0010](原码) = -2

有符号:16/8 = -2
2^3=8
[0001 0000](补码)=> 算数移位>>>3  = [0000 0010](补码) => [0000 0010](反码) =>[0000 0010](原码) = 2

100 101 1001 0110 逻辑移位出现小数,总是舍入到零,比如 7/2应该是 3,而不是4

浮点数

浮点数的二进制表示:浮点数二进制分为3块:第一位表示正负 第二块表示2的多少次方 第三块表示谁*2的N次方

IEEE 浮点标准表示: V = (-1)s * M * 2^E 。

  1. s 是符号位,为0时表示正,为1时表示负。
  2. M为尾数,是一个二进制小数,它的范围是0至1-ε,或者1至2-ε(ε的值一般是2-k次方,其中设k > 0)
  3. E为阶码,可正可负,作用是给尾数加权。

二进制表示方法:我们将浮点数的位划分为三个阶段,分别对这些值进行编码。

  1. 一个单独的符号位 s 直接编码符号 s 【表示正负】
  2. k 位的阶码字段 exp =ek-1ek-2…e1e0 编码阶码E [表示2的多少次方,但是需要再+127,是因为计算机的表示形式导致的,下面有解释]
  3. n 位小数字段 frac = fn-1fn-2…f1f0 编码尾数 M,但是编码出来的值也依赖于阶码字段的值是否等于0. 【表示谁乘以2的E次方,用补码表示】

前面说过,1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字 E为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值必须再减去一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。 比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。

为这种表示方法限制了浮点数的范围和精度,浮点数只能近似的表示一个数。 比如 数字1/5,我们能用十进制小数 0.2 准确的表示,但是我们却不能把它准确的表示为一个二进制小数,我们只能通过增加二进制表示的长度来提高表示的精度。如下:

一种简单的二进制推算方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 例如:3.4
 个位数位3 二进制表示为 11

 小数位,由于二进制小树比十进制表示的更小,所以采用这种方式:(小数部分*2,然后取结果的整数部分作为当前的第一位,然后小数部分继续*2,依次进行累加,直到小数部分也变为0的时候停止(或者达到位的上线),如果不够23位则后面补0即可)
 0.4*2=0.8 整数位为0,所以小数位第一位: 0
 然后 0.8*2 = 1.6 整数位为1,所以小数位第二位: 1
 0.6*2=1.2 第三位:1
 0.2*2=0.4 第四位:0
 依次进行累加。。。。。
 最终它的二进制变成了 :11.0110011001100110
 **基数需要从二进制数的第一个1开始表示(所以,如果是0.11的话,就要右移成1.1这样,E就需要-1而不是+1了就变成了 127-1)**,所以 **左移1位** 就变成了1.10110011001100110011001
 因为是正数,所以S位是0 阶位就是刚才左移的1 尾数因为要统一去掉1(因为所有的第一位都是1,而且,这样,就能多一位尾数来表示更精确的小数),所以最终的二进制就变为了
 0 0001 10110011001100110011001
 但是注意我们之前说的:**E为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值必须再减去一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。**
 所以S应该是127+1=128所以,最终的二进制就变为了
 0 10000000 10110011001100110011001

 
参考

深入理解计算机系统(2.7)——二进制小数和IEEE浮点标准 浮点数的二进制表示 浮点数的二进制表示(IEEE 754标准) 浮点数的二进制表示以及与十进制的相互转换 计组_浮点数

浮点数的运算

加减法:注意,尾数是补码表示的

  1. s位 有啥用???? 符号化的话,都有尾数来表示了?????? (自己代码跑了下,貌似没啥用了,无论正负都显示的是ffff)
  2. E位(次方) 取大的
  3. E位 两个相减 获取到阶差
  4. 对小的阶位 的尾数(M) 右移 (上一步算的 阶差位)
  5. 然后看右移舍去的最高位是0 还是 1 如果是1则需要在 之后的尾数的最后一位+1
  6. 尾数进行加减法
  7. 规格化处理:尾数进行运算的结果必须变成规格化的浮点数,对于双符号位(就是使用00表示正数,11表示负数,01表示上溢出,10表示下溢出)的补码尾数来说,就必须是 001×××…×× 或110×××…××的形式
  8. 最终组合起来就是 最终 浮点数 的二进制

溢出处理 浮点数的溢出是以其阶码溢出表现出来的。在加\减运算过程中要检查是否产生了溢出:若阶码正常,加(减)运算正常结束;若阶码溢出,则要进行相应处理。另外对尾数的溢出也需要处理。 阶码上溢 超过了阶码可能表示的最大值的正指数值,一般将其认为是+∞和-∞。 阶码下溢 超过了阶码可能表示的最小值的负指数值,一般将其认为是0。 尾数上溢 两个同符号尾数相加产生了最高位向上的进位,将尾数右移,阶码增1来重新对齐。 尾数下溢 在将尾数右移时,尾数的最低有效位从尾数域右端流出,要进行舍入处理。

1
2
3
4
5
6
7
8
9
10
11
12
//例如:下面的二进制相加进行计算
2.25+1.0=3.25
1. 二进制
  符号位 阶码(对应阶码的反码)       尾数
X   0   10000000        00100000000000000000000
Y   0   01111111        00000000000000000000000
2.求阶差  100000000 - 01111111 = 1
3.Y的阶码比X小1位
4.Y的尾数就相应右移1位(这样X跟Y的阶乘就抹平了)  00000000000000000000000->10000000000000000000000
5.尾数做处理,采用0舍1入法处理 10000000000000000000000=>10000000000000000000000
5.尾数相加 00100000000000000000000 + 10000000000000000000000 = 10100000000000000000000
7.最终的二进制就变为了 0 10000000 10100000000000000000000

乘除法

  1. s位 有啥用???? 符号化的话,都有尾数来表示了?????? (自己代码跑了下,貌似没啥用了,无论正负都显示的是ffff)
  2. E位(次方) 乘法求和 除法求差(其实是进行加减)【注意,不能直接用阶位相加,因为阶位之前加过127,所以相加后要减去一个127】
  3. 尾数相乘(其实是M相乘)
  4. 规则化处理
  5. 溢出处理
  6. 最终组合起来就是 最终 浮点数 的二进制
1
2
3
4
5
6
7
8
9
10
11
12
13
例如:下面的二进制相乘 1.25*2.0=2.5
1.转化为2进制
0 01111111 01000000000000000000000
0 10000000 00000000000000000000000

<<<<<<< HEAD
0 11111111 01000000000000000000000
=======
2.尾数相乘,阶位相加
   2+0=2   1.01*1.0
>>>>>>> c9190d314aab182f89675afe8c9a9626afde2f0e
0 10000000 01000000000000000000000

参考

深入理解计算机系统(2.7)——浮点数舍入以及运算 浮点数的表示和基本运算 浮点数运算中的舍入问题 «««< HEAD 浮点加法、减法, 乘法、除法运算

1100110 1101101

1011 0101

11111 11100 1 11011 ======= 浮点加法、减法, 乘法、除法运算

c9190d314aab182f89675afe8c9a9626afde2f0e


--EOF--

若无特别说明,本站文章均为原创,转载请保留链接,谢谢