usort排序含float值的数组时出现不符合预期的结果
问题根源:usort回调的返回值要求是整数,浮点数精度坑了你
这个问题其实是浮点数精度限制和usort的回调函数规则共同导致的:
usort的回调函数明确要求返回整数:
- 返回负数:$a排在$b前面
- 返回正数:$b排在$a前面
- 返回0:两者相对顺序保持不变
而你直接用$a['custom_price'] - $b['custom_price']得到的是浮点数,PHP会自动把这个浮点数转为整数。当两个浮点数的差值非常接近1但略小于1时(比如0.9999999999999964),转成整数就会变成0,usort就会认为这两个元素相等,不会调整它们的顺序。
你的测试案例里发生了什么?
你直观认为16.83 - 15.83 = 1,但实际上由于浮点数的二进制存储精度问题,这个计算结果可能是一个略小于1的浮点数,转成整数后就是0。usort看到返回0,就保留了这两个元素原来的相对顺序(1176在1199前面),最终导致排序结果异常。
而当你把15.83改成14.83时,16.83 - 14.83 = 2,这个结果是精确的浮点数,转成整数是2,usort会正确把14.83的元素排在前面。
解决方法
有几种靠谱的修复方式:
1. 使用PHP 7+的太空船运算符(推荐)
太空船运算符会自动处理浮点数比较,返回符合要求的整数结果:
usort($array, function($a, $b) { return $a['custom_price'] <=> $b['custom_price']; });
2. 手动判断返回-1/0/1
完全避免浮点数减法的精度问题,逻辑更清晰:
usort($array, function($a, $b) { if ($a['custom_price'] < $b['custom_price']) { return -1; } elseif ($a['custom_price'] > $b['custom_price']) { return 1; } else { return 0; } });
3. 对差值取整(注意适用场景)
如果确定你的价格差值不会小于0.5,可以用round把结果转成整数:
usort($array, function($a, $b) { return round($a['custom_price'] - $b['custom_price']); });
用上面任意一种方法,你的测试数组都会按照15.83 → 16.83 → 33.15的正确顺序排序。
内容的提问来源于stack exchange,提问作者Bilal Ahmed




