计算机中的二进制表示(定点数,浮点数)

Table of Contents

1 定点数二进制表示

在计算机内,有符号数有3中表示法:原码、反码、补码。

1.1 规则及表示方法

首先是对有符号数而言:

  1. 二进制的最高位是符号位:0–>正,1–>负
  2. 正数的原码,反码,补码一样
  3. 负数的反码==原码的符号位不变,其他的位取反
  4. 负数的补码==反码+1
  5. 0的反码,补码都是0。数值0的补码只有一个,即:0的补码=00000000B
  6. 计算机运算的时候都是以补码的方式运算的。

1.2 补充

  1. (-128)没有相应的原码和反码。(-128)=(1000 0000)补码
  2. 采用补码的原因:
    1. 使用补码可以使符号位与其他位统一进行处理。
    2. 减法可以按照加法处理。如果最高位(符号位)有进位,则进位就舍弃。
  3. 已知补码,求原码:补码的补码。(因为:对于二进制来说先减1后取反和先取反后加1得到的结果是一样的)

2 浮点数二进制表示

根据国际标准IEEE 754,任意一个二进制浮点数V可以表示成下面的形式:

V = (-1)s * M * E

  1. (-1)s 表示符号位,当s=0,V为正数;当s=1,V为负数。
  2. M表示有效数字,大于等于1,小于2。
  3. 2E 表示指数位。

举例来说:十进制的-5.0,写成二进制是-101.0,相当于-1.01×22 。那么,s=1,M=1.01,E=2。

IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

 
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。


2.1 规则及表示方法

IEEE 754对有效数字M和指数E,还有一些特别规定。

前面说过,1≤M<2,也就是说,M可以写成1.xxxxxx的形式,其中xxxxxx表示小数部分。IEEE 754规定,在计算机 内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只 保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给 M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。

至于指数E,情况就比较复杂。

首先,E为一个无符号整数(unsigned int)。这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它 的取值范围为0~2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,E的真实值必须 由E再减去一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
比如,210 的E是10,所以保存成32位浮点数时,必须保存成10(E的真实值)+127=137(E),即10001001。

然后,指数E还可以再分成三种情况:
(1)E不全为0或不全为1。这时,浮点数就采用上面的规则表示,即指数E的计算值减去127(或1023),得到真实 值,再将有效数字M前加上第一位的1。
(2)E全为0。这时,浮点数的指数E等于1-127(或者1-1023),有效数字M不再加上第一位的1,而是还原为 0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
(3)E全为1。这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s);如果有效数字M不全为0,表示 这个数不是一个数(NaN)。

2.2 平方根倒数速算法(不得不提到Quake-III Arena (雷神之锤3))

此段转自http://blog.renren.com/GetEntry.do?id=491777510&owner=245298353
Quake-III Arena (雷神之锤3)是90年代的经典游戏之一。该系列的游戏不但画面和内容不错,而且即使计算机配 置低,也能极其流畅地运行。这要归功于它3D引擎的开发者约翰-卡马克(John Carmack)。事实上早在90年代初 DOS时代,只要能在PC上搞个小动画都能让人惊叹一番的时候,John Carmack就推出了石破天惊的Castle Wolfstein, 然后再接再励,doom, doomII, Quake…每次都把3-D技术推到极致。他的3D引擎代码资极度高效,几 乎是在压榨PC机的每条运算指令。当初MS的Direct3D也得听取他的意见,修改了不少API。

最近,QUAKE的开发商ID SOFTWARE 遵守GPL协议,公开了QUAKE-III的原代码,让世人有幸目睹Carmack传奇的3D引 擎的原码。这是QUAKE-III原代码的下载地址: http://www.fileshack.com/file.x?fid=7547

(下面是官方的下载网址,搜索 “quake3-1.32b-source.zip” 可以找到一大堆中文网页的。 ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip)

我们知道,越底层的函数,调用越频繁。3D引擎归根到底还是数学运算。那么找到最底层的数学运算函数(在 game/code/qmath.c), 必然是精心编写的。里面有很多有趣的函数,很多都令人惊奇,估计我们几年时间都学 不完。在game/code/qmath.c里发现了这样一段代码。它的作用是将一个数开平方并取倒,经测试这段代码比 (float)(1.0/sqrt(x))快4倍:

 1 float Q_rsqrt( float number )
 2 {
 3         long i;
 4         float x2, y;
 5         const float threehalfs = 1.5F;
 6  
 7         x2 = number * 0.5F;
 8         y  = number;
 9         i  = * ( long * ) &y;                       // evil floating point bit level hacking
10         i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
11         y  = * ( float * ) &i;
12         y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
13 //      y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed
14  
15         return y;
16 }

 

函数返回1/sqrt(x),这个函数在图像处理中比sqrt(x)更有用。 注意到这个函数只用了一次叠代!(其实就是根本没用叠代,直接运算)。编译,实验,这个函数不仅工作的很好, 而且比标准的sqrt()函数快4倍!要知道,编译器自带的函数,可是经过严格仔细的汇编优化的啊! 这个简洁的函数,最核心,也是最让人费解的,就是标注了“what the fuck?”的一句 i = 0x5f3759df - ( i >> 1 );

再加上y = y * ( threehalfs - ( x2 * y * y ) ); 两句话就完成了开方运算!而且注意到,核心那句是定点移位运算,速度极快!特别在很多没有乘法指令的RISC结构CPU上,这样做是极其高效的。

算法的原理其实不复杂,就是牛顿迭代法,用x-f(x)/f'(x)来不断的逼近f(x)=a的根。

没错,一般的求平方根都是这么循环迭代算的但是卡马克(quake3作者)真正牛B的地方是他选择了一个神秘的常数 0x5f3759df 来计算那个猜测值,就是我们加注释的那一行,那一行算出的值非常接近1/sqrt(n),这样我们只需要 2次牛顿迭代就可以达到我们所需要的精度。

想了解更多: http://zh.wikipedia.org/wiki/%E5%B9%B3%E6%96%B9%E6%A0%B9%E5%80%92%E6%95%B0%E9%80%9F%E7%AE%97%E6%B3%95#.E2.80.9C.E9.AD.94.E6.9C.AF.E6.95.B0.E5.AD.97.E2.80.9D https://sites.google.com/site/winddeep/sqrt

Date: 2012-08-17 17:44:15 中国标准时间

Author: Crowning

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0
posted @ 2012-08-17 17:55 csqlwy 阅读(...) 评论(...) 编辑 收藏