浮点数的表示方法
计算机语言使用二进制存储十进制数据,在PHP
中 double
和 float
是相同的,由于一些历史的原因,这两个名称同时存在。PHP
使用 IEEE 754
双精度格式
将一个十进制数值转换为二进制数值,需要通过下面的计算方法:
1. 整数部分:连续用该整数除以2,取余数,然后商再除以2,直到商等于0为止。然后把得到的各个余数按相反的顺序排列。简称”除2取余法“。
2. 小数部分:十进制小数转换为二进制小数,采用”乘2取整,顺序排列”法。用2乘以十进制小数,将得到的整数部分取出,再用2乘余下的小数部分,然后再将积的整数部分取出,如此进行,直到积中的小数部分为0或者达到所要求的精度为止。然后把取出的整数部分按顺序排列起来,即先取出的整数部分作为二进制小数的高位,后取出的整数部分作为低位有效位。简称”乘2取整法“。
浮点数的精度
以十进制能够精确表示的有理数如 0.1
或 0.7
,无论有多少尾数都不能被内部所使用的二进制精确表示,因此不能在不丢失一点点精度的情况下转换为二进制的格式。
<?php
sprintf('%.53f', 0.1);//0.10000000000000000555111512312578270211815834045410156
sprintf('%.53f', 0.7);//0.69999999999999995559107901499373838305473327636718750
浮点数的运算
由于浮点数的精度问题,进行运算时会造成混乱的结果:
<?php
var_dump(0.1 + 0.7 == 0.8);//false
var_dump(abs(0.1 + 0.7 - 0.8) > 0);//true
sprintf('%.53f',0.1 + 0.7);//0.79999999999999993338661852249060757458209991455078125
sprintf('%.53f',round(0.1 + 0.7,5));//0.80000000000000004440892098500626161694526672363281250
sprintf('%.53f',round(0.1 + 0.7,16));//0.79999999999999993338661852249060757458209991455078125
floor((0.1 + 0.7) * 10);//7
所以永远不要相信浮点数结果精确到了最后一位,也永远不要比较两个浮点数是否相等。
使用 BC 数学函数
- bcscale — 设置所有bc数学函数的默认小数点保留位数
- bcadd — 2个任意精度数字的加法计算
- bcsub — 2个任意精度数字的减法
- bcmul — 2个任意精度数字乘法计算
- bcdiv — 2个任意精度的数字除法计算
- bccomp — 比较两个任意精度的数字
- bcpow — 任意精度数字的乘方
- bcsqrt — 任意精度数字的二次方根
- bcmod — 对一个任意精度数字取模