关于PHP浮点数常量PHP_FLOAT_EPSILON与PHP_FLOAT_DIG的理解困惑
嘿,我完全懂你为啥会对这两个常量犯迷糊——浮点数的精度问题本身就有点反直觉,官方文档的定义又太生硬,咱们掰开揉碎了说:
先聊PHP_FLOAT_EPSILON
你的疑惑点特别对:明明1.0 + PHP_FLOAT_EPSILON看起来还是1.0,但官方说它不等于1.0?其实问题出在显示精度和实际存储值的区别上。
PHP_FLOAT_EPSILON的值是2.2204460492503E-16,这是双精度浮点数能区分的、和1.0最接近的那个差值。当你把它加到1.0上,得到的数在二进制存储里确实和原来的1.0不一样,但因为十进制显示的时候只会输出有限位数(默认是几位小数),所以看起来还是1.0。你可以试试用更高精度的输出方式验证:
$sum = 1.0 + PHP_FLOAT_EPSILON; printf("%.20f\n", $sum); // 会输出1.00000000000000022204 var_dump($sum != 1.0); // 结果是bool(true)
这个常量的实际用处是判断两个浮点数是否“足够相等”——因为浮点数计算经常会有精度误差,直接用==比较很容易出问题,比如0.1 + 0.2并不等于0.3,这时候就可以判断两个数的差值是否小于PHP_FLOAT_EPSILON,以此来认为它们相等。
再讲PHP_FLOAT_DIG
官方的定义翻译过来确实绕,换个直白的说法:这是一个安全的十进制位数阈值——如果你有一个十进制数,它的有效数字位数不超过PHP_FLOAT_DIG(通常是15位),那么把它转成float类型,再转回十进制数,得到的结果和原来的数完全一样,不会有精度损失。
举个例子:
- 15位有效数字的数:
123456789012345,转成float再转回十进制,还是123456789012345 - 16位有效数字的数:
1234567890123456,转成float再转回,可能就变成1234567890123456.0以外的数了(具体取决于二进制存储的精度)
原因是双精度浮点数的二进制有效位数是53位,换算成十进制大概是15-17位,但PHP_FLOAT_DIG取的是最保守的15位——保证在这个范围内的十进制数,转成float再转回来不会“变味”。这个常量能帮你知道:当处理小数或者大整数时,多少位以内的数可以放心用float存储,不用担心来回转换丢失精度。
备注:内容来源于stack exchange,提问作者divad

