一、array_product()是做什么的
PHP里算数组元素之和有array_sum(),算乘积有array_product()。后者把数组里每个元素依次相乘,返回最终结果。
函数原型:
number array_product(array $array);
参数只有一个,必填,是要计算的数组。返回类型是int或float,取决于计算结果是否超出整数范围或包含浮点数。
基础用法:
$prime = array(2, 3, 5, 7);
echo array_product($prime); // 210
2×3×5×7=210。数组有几个元素就乘几次,顺序不影响结果。
浮点数也能算:
$num = array(1.1, 2.2, 3.3);
echo array_product($num); // 7.986
结果返回float。只要数组里有一个浮点数,最终结果通常就是float,除非乘积恰好是整数值。
二、空数组返回1,这个设计有讲究
对空数组调array_product(),返回1,不是0。
$empty = array();
var_dump(array_product($empty)); // int(1)
这个行为来自数学上的“空积”约定。乘法单位元是1,空数组没有元素可乘,返回单位元1,这样嵌套计算时不会破坏结果。对比array_sum(),空数组返回0,因为加法的单位元是0。
我在做一个统计模块时踩过这个坑。代码逻辑是“把一组比例因子相乘得到总系数”,数组从数据库查出来可能是空的。当时想当然以为空数组返回0,结果总系数变成1,后续计算全偏了。后来加了判断:
$factors = array();
if (empty($factors)) {
// 单独处理,不直接乘
} else {
$total = array_product($factors);
}
空数组返回1这件事,用之前得知道,否则容易在业务逻辑里留下隐患。
三、非数值元素会被隐式转换
array_product()不检查元素类型,它按PHP的数值转换规则处理每个元素。
$mixed = array(2, "3", "code", true, null);
echo array_product($mixed); // 0
逐个看:
-
2是整数2
-
"3"是数字字符串,转成3
-
"code"不是数字字符串,转成0
-
true转成1
-
null转成0
乘到"code"的时候结果已经是0,后面全是0。所以最终返回0。
如果数组里混了非数值字符串,结果往往是0,但这不代表计算“失败”,函数只是按规则转换了。如果业务上不允许非数值元素,得自己先过滤:
$data = array(2, "3", "code", 5);
$filtered = array_filter($data, 'is_numeric');
echo array_product($filtered); // 30
array_filter()配合is_numeric()能把非数值元素去掉。这一步在数据来自用户输入或外部接口时比较有必要。
四、大数溢出和精度问题
PHP的整数有范围限制,64位系统上较大约9.2×10^18。乘积超过这个范围,结果会自动转成float。
$big = array(1000000, 1000000, 1000000);
var_dump(array_product($big)); // float(1.0E+18)
三个一百万相乘是10^18,接近整数上限但还没超,PHP可能返回int也可能返回float,取决于具体平台和版本。再多乘一个就明确是float了。
$bigger = array(1000000, 1000000, 1000000, 1000000);
var_dump(array_product($bigger)); // float(1.0E+24)
float有精度限制,大约15到17位有效数字。超过之后结果不再精确。做阶乘、排列组合这类计算时,大数乘积用array_product()只能得到近似值。
需要精确大数运算,得用BCMath或GMP扩展:
$nums = array("1000000", "1000000", "1000000", "1000000");
$result = "1";
foreach ($nums as $n) {
$result = bcmul($result, $n);
}
echo $result; // 1000000000000000000000000
BCMath用字符串存数字,不受浮点精度影响。代价是速度比原生运算慢。如果业务对精度有要求,比如金额、库存、科学计算,别用array_product()。
五、和array_sum()的对称与差异
array_product()和array_sum()是一对,一个乘一个加,但行为上有几处不同:
| 对比项 | array_product() | array_sum() |
|---|---|---|
| 空数组返回 | 1 | 0 |
| 非数值字符串 | 转0,乘积为0 | 转0,不影响和 |
| 大数溢出 | 转float,可能失精 | 转float,可能失精 |
| 返回类型 | int或float | int或float |
关键差异在非数值元素的影响。array_sum()里一个"code"转成0,其他数值照常相加,结果可能还有意义。array_product()里一个"code"转成0,整个乘积直接归零。这个差别让array_product()对数据质量更敏感。
我在处理用户提交的多选评分时遇到过类似情况。评分数组里混了空字符串,用array_product()算总权重,结果全是0。排查后发现是空选项没过滤。后来在入口处统一做了is_numeric校验,问题不再出现。
六、项目里的实际用法
计算折扣系数。电商场景里,多个折扣叠加,总折扣是各折扣率相乘:
$discounts = array(0.9, 0.95, 0.8);
$finalRate = array_product($discounts); // 0.684
九折、九五折、八折叠加,最终是原价的68.4%。这个用法比循环乘更简洁。
计算阶乘。给定n,生成1到n的数组,乘起来就是n的阶乘:
$n = 5;
$nums = range(1, $n);
echo array_product($nums); // 120
n小的时候够用,n大了会遇到溢出和精度问题,换BCMath。
计算概率连乘。多个独立事件同时发生的概率是各自概率的乘积:
$probs = array(0.5, 0.4, 0.3);
echo array_product($probs); // 0.06
这种场景结果通常很小,float精度够用。
几何平均数的中间步骤。几何平均数是所有数乘积的n次方根,array_product()可以算乘积部分:
$values = array(2, 8, 4);
$product = array_product($values); // 64
$geometricMean = pow($product, 1 / count($values)); // 4
一个踩坑记录。有次做库存预警,逻辑是“多个仓库库存比例相乘得到总体可用率”。数据库里某个仓库的比例字段是NULL,查出来放进数组,array_product()把NULL转成0,整个可用率变成0,预警全触发。排查时才发现NULL没处理。后来在查询层用COALESCE把NULL转成1,或者在PHP层用array_filter()过滤掉NULL,问题解决。
这个经历让我形成一个习惯:调array_product()之前,先确认数组里没有NULL、空字符串、非数值字符串。这些值会悄悄把结果变成0,而函数本身不会报错。
七、本节课程知识要点
-
array_product()计算数组所有元素的乘积,返回int或float。
-
空数组返回1,这是乘法的单位元,不是错误,但业务上要留意。
-
非数值元素按PHP转换规则处理,非数值字符串转0,导致乘积归零。
-
大数乘积会转float,超过约15到17位有效数字后精度下降。
-
需要精确大数运算用BCMath或GMP,不要依赖array_product()。
-
和array_sum()的关键差异:空数组返回值不同,非数值元素对结果的影响程度不同。
-
调之前先过滤数据,用array_filter()配合is_numeric()排除无效元素,避免结果意外归零。
array_product()的用法直接,但它的几个隐式行为——空数组返回1、非数值转0、大数转float——决定了它在项目里不能拿来就用。数据来源越杂,越要先过滤。精度要求越高,越要考虑BCMath。它适合处理数值干净、量级可控的数组,比如折扣率、概率、小范围阶乘。超出这个范围,手写循环或用专门的数学库更稳。