代码改变世界

定点数的表示

2012-07-18 16:40  彭白洋  阅读(2683)  评论(1编辑  收藏  举报

定点数的表示

所有的定点数学实际上是以整数尺度为基础的。比如,我们想用一个整数来表示10.5。这做不到,因为没有小数位。你可以将其截断为10.0或将其舍入为11.0,但10.5不是一个整数。但如果你将10.5放大10倍,10.5就变成了105.0,这是一个整数。这便是定点数的基础。你可以采用某一比例系数来对数值进行缩放,并在进行数学计算时将比例系数考虑进去。

由于计算机是二进制的,大部分游戏程序倾向于使用32位整数(或int),以16.16的格式来表示定点数。


你可以将整数部分放在高16位,小数部分置于低16位。这样你已将整个数值放大为原来的2^16即65536倍。另外,为提取出一个定点数的整数部分,你可以移位并屏蔽掉高16位;为得到小数部分,你可以移位并屏蔽掉低16位。

以下是一些常用的定点数的类型:

#define FP_SHIFT 16 // shifts to produce a fixed-point number

#define FP_SCALE 65536 // scaling factor

typedef int FIXPOINT;

在定点与浮点之间转换

有两类数需要转换为定点数:整数和浮点数。这两类转换是不同的,需分别考虑。对于整数,直接用其二进制的补码表示。所以你可以移位操作来放大这个数,从而将其转换为定点数。而对于浮点数,由于其使用IEEE格式,四字节中有一个尾数和指数,因此移位将破坏这个数。因此,必须使用标准的浮点乘法来进行转换。

数学

二进制补码是一种表示二进制整数的方法。因此整数和负数均可以用这种方法表示,并且在该集合上数学运算都是正确的。一个二进制数的补码是指将该二进制的每一位取反并与1进行加运算所得的数。在数学意义上,假定你想求6的补码,先取反码得-6,再与1相加即-6+1或~0110+0001=1001+0001=1010。该数就是10的普通二进制表示,但同时也是-6的补码表示。

以下是将整数转换为定点数的宏:

#define INT_TO_FIXP(n) (FIXPOINT((n << FP_SHIFT)))

例如:

FIXPOINT speed = INT_TO_FIXP(100);

下面是将浮点数转换为定点数的宏:

#define FLOAT_TO_FIXP(n) (FIXPOINT((float)n * FP_SCALE))

例如:

FIXPOINT speed = FLOAT_TO_FIXP(100.5);

提取一个定点数也很简单。下面是从高16位提取整数部分的宏:

#define FIXP_INT_PART(n) (n >> 16)

至于从低16位提取小数部分,则只需将整数部分屏蔽掉即可:

#define FIXP_DEC_PART(n) (n & 0x0000ffff)

当然,如果你聪明的话,可以不需要进行转换,只要使用指针即时地访问高16位和低16位即可。如下所示:

FIXPOINT fp;

short *integral_part = &(fp+2), *decimal_part = &fp;

指针integral_part和decimal_part总是指向你所需的16位数。