array_filter直接比较浮点数会漏值,因IEEE 754导致0.1+0.2≠0.3(实际为0.30000000000000004),==/===均不可靠;应改用abs($v - 0.3) zuojiankuohaophpcn $epsilon判断。

为什么 array_filter 直接比较会漏掉“看似相等”的浮点数
PHP 中浮点数无法精确表示(如 0.1 + 0.2 不等于 0.3),导致用 == 或 === 做数组筛选时,本该匹配的值被跳过。这不是 PHP 特有,而是 IEEE 754 浮点标准的共性问题。
常见错误写法:
$arr = [0.1 + 0.2, 0.3, 0.6 - 0.3];
$result = array_filter($arr, function($v) { return $v == 0.3; }); // 可能只返回一个元素,甚至空数组
- 根本原因:
0.1 + 0.2实际是0.30000000000000004,与字面量0.3的二进制表示不完全一致 -
==在浮点场景下不可靠;===更严格,更不可能命中 - 不要依赖
round($v, 10) == round(0.3, 10)——round()在边界值(如0.049999999999999996)可能四舍五入失真
用 abs($a - $b) 替代相等判断
这是最通用、最可控的浮点容差比较方式。关键在于选对 $epsilon —— 它不是固定常量,而应根据数据量级和精度需求动态设定。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 对于小数点后 1–2 位的业务数据(如金额、评分),用
1e-6到1e-8足够安全 - 若原始数据本身只有 2 位小数(如
12.34),可设$epsilon = 1e-3(即允许 ±0.001 偏差) - 避免硬编码
0.000001,统一用科学计数法1e-6,更易读且无类型歧义 - 示例筛选所有“接近 0.3”的值:
$target = 0.3;
$epsilon = 1e-8;
$result = array_filter($arr, function($v) use ($target, $epsilon) {
return abs($v - $target) < $epsilon;
});
处理数组中混合类型或含 null/false 的情况
array_filter() 默认会过滤掉 false、0、""、null 等“falsy”值,这在浮点筛选中极易误伤 —— 比如 0.0 是合法浮点数,但会被默认行为剔除。
- 必须显式传入
ARRAY_FILTER_USE_BOTH或使用回调函数,并确保返回布尔值而非原始值 - 绝对不要写
array_filter($arr, 'abs')——abs(0.0)返回0,被当作false过滤掉 - 若数组含非数值项(如字符串
"0.3"),先用is_numeric()或filter_var($v, FILTER_VALIDATE_FLOAT)预检,否则abs($v - $target)可能触发警告或返回意外结果 - 稳妥写法:
$result = array_filter($arr, function($v) use ($target, $epsilon) {
if (!is_numeric($v)) return false;
return abs((float)$v - $target) < $epsilon;
});
性能与可读性兼顾:封装成复用函数
重复写 abs($v - $target) 易出错且难维护。封装时注意两点:不隐藏容差逻辑,不牺牲调试便利性。
- 函数名明确体现“容差”,例如
float_in_range(),而非模糊的is_equal() - 把
$epsilon设为可选参数,默认值设为1e-8,但文档/注释里强调“请按需调整” - 避免在函数内做类型强制转换(如自动
(float)),让调用方决定如何清洗输入 - 示例:
function float_array_filter(array $arr, float $target, float $epsilon = 1e-8): array
{
return array_filter($arr, function($v) use ($target, $epsilon) {
return is_numeric($v) && abs($v - $target) < $epsilon;
});
}
真正容易被忽略的是:当数组元素本身来自不同计算路径(比如有的来自 bcadd(),有的来自普通运算),它们的误差分布并不均匀。这时候单一 $epsilon 可能不够 —— 你需要先用 var_dump(array_map('sprintf', $arr, array_fill(0, count($arr), '%.17g'))) 看清实际存储值,再决定容差策略。











