一、序言
如果用php的+-*/计算 浮点数 的时候,可能会遇到一些计算结果错误的问题,比如echo intval( 0.58*100 );会打印57,而不是58,这个其实是计算机底层 二进制 无法精确表示浮点数的一个bug,是跨语言的,比如说 python 也会遇到这个问题。
例子:
二、浮点数表示规则
为啥输出是57? 首先我们要知道浮点数的表示(IEEE 754):
浮点数, 以64位的长度(双精度)为例, 会采用1位符号位(E), 11 指数 位(Q), 52位尾数(M)表示(一共64位).
符号位:最高位表示数据的正负,0表示正数,1表示负数。
指数位:表示数据以2为底的幂,指数采用偏 移码 表示
尾数:表示数据小数点后的有效数字.
三、浮点数二进制表示方式
这里的关键点就在于, 小数在二进制的表示方式:
将该数字乘以2,取出整数部分作为二进制表示的第1位;然后再将小数部分乘以2,将得到的整数部分作为二进制表示的第2位;以此类推,直到小数部分为0。
以0.625为例:
0.625* 2 = 1.25 —————- 1
0.25 * 2 = 0.5 —————— 0
0.5 *2 = 1.0 ——————- 1
小数部分为0,停止运算,0.625用二进制表示就是 0.101
以0.58为例:
0.58 * 2 = 1.16 ——————- 1
0.16 * 2 = 0.32 ——————- 0
0.32 * 2 = 0.64 ——————- 0
0.64 * 2 = 1.28 ——————- 1
0.28 * 2 = 0.56 ——————- 0
0.56 * 2 = 1.12 ——————- 1
0.12 * 2 = 0.24 ——————- 0
0.24 * 2 = 0.48 ——————- 0
…..
小数部分出现循环,无法停止,但是由于尾数最长只有52位,所以无法准确的表示0.57这个数,在从 二进制转换 回十进制时就是0.579999999… 那你*10之后intval一下, 自然就是57了….
四、结语
可见这个问题的关键点就是: “你看似有穷的小数, 在计算机的二进制表示里却是无穷的” so, 不要再以为这是PHP的bug了, 这就是这样的…..