You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Ruby中array.sum与array.inject(:+)结果不同的原因探究

为什么Ruby中Array#sumArray#inject(:+)求和结果不同?

这是个很典型的浮点数精度问题,核心在于两个方法处理数值累加的逻辑完全不一样:

1. inject(:+)的工作方式

inject(:+)的逻辑非常直白——它会严格按数组顺序把元素逐个相加,完全依赖Ruby默认的浮点数运算规则。

但问题出在:像1.1、6.16这类十进制浮点数,根本无法被精确表示为二进制浮点数,它们在内存中存储的本身就是近似值。当你用inject(:+)逐次累加这些近似值时,误差会被不断传递和放大,最终得到的结果就会和预期的十进制值产生细微偏差,也就是你例子里的17.259999999999998

2. Array#sum的优化逻辑

Ruby从2.4版本开始引入的Array#sum方法,针对浮点数求和做了专门的精度优化。它内部会采用更智能的方式来减少浮点数运算的误差:

  • 一种常见的实现思路是先将浮点数转换为**有理数(Rational)**进行中间计算,有理数可以精确表示十进制小数的分数形式,累加完成后再转换回浮点数;
  • 或者使用误差补偿算法(比如Kahan求和),在累加过程中实时记录并修正误差,让最终结果更接近真实的十进制值。

所以在你的例子里,sum方法能给出更符合预期的17.26

验证这个逻辑

你可以手动模拟sum的精确计算过程,结果会和a.sum完全一致:

a = [10, 1.1, 6.16]
# 把浮点数转成有理数再累加,最后转浮点数
a.map(&:to_r).inject(:+).to_f # => 17.26

总结

  • 如果是整数求和或者对精度要求不高的场景,inject(:+)完全够用;
  • 如果是浮点数求和且希望结果更贴近预期的十进制值,优先使用Array#sum

内容的提问来源于stack exchange,提问作者Ilya Cherevkov

火山引擎 最新活动