有趣的浮点数
浮点数编码
从高到低分别是:符号位、指数位、有效数位
float:4字节,符号1位、指数8位、有效数23位
double:8字节,符号1位、指数11位、有效数52位
二进制表示
以4字节的float为例,整数部分采用除2取余法,小数部分采用乘2取整法,然后严格按照以下步骤编码:
- 正数或0符号位0,负数符号位1
- 移动小数点到第一个有效数字的右边,假定移动N位,左移N为正,右移N为负
- 若N为正,则指数位的最高位为1,剩余7位则用N-1的二进制来填充
- 若N为负或0,则指数位的最高位为0,剩余7位则用N的二进制取反来填充
- N的取之范围[-127, 127],当N为128时,则为无穷数
- 小数点右边的其余数字,依次填充有效数位即可,多余数字截断
- 为了表示更小的小数,当右移位数大于等于127位时,只移127位,此时需要借用有效数位的最高位来区分小数点前是0还是1,因此此时的有效数位少一位
取值范围
从以上分析可得出float的取值范围:最大数为1.999...pow(2, 127),最小数为pow(2, -127-22)
double的取值范围:最大数为1.999...pow(2, 1023),最小数为pow(2, -1023-51)精度
以整数为例,对于float来说,能精确表示的最大整数为pow(2, 24)
对于不在精度范围内的数,编译器和cpu采用“五舍六入”法游戏采用浮点数的误区
游戏内经常会判断两个float是否相等,采用差值的绝对值小于一个很小的数delta,这种是不严谨的,由于float的最小粒度为pow(2, -23),精确到小数点后7位,是在整数部分只占最多1bit的情况下,假设整数部分为10000,此时若delta为0.0001,会超出精度,应改成delta为0.001,可采用以下函数来判断:
bool is_float_eq(float x, float y) { return x == y || abs(*(unsigned int*)&x-*(unsigned int*)&y) == 1; }