Scala中对超长List[Double]求平均避免出现Infinity的方案
解决Double列表求和溢出导致平均值为Infinity的问题
你遇到的核心问题是Double类型的数值范围限制:Double的最大值约为1.7976931348623157E308,当你的列表元素数量多且单个数值较大时,求和会超出这个上限,直接变成Infinity,后续除以长度自然还是Infinity。下面给你两种实用的解决方案,你可以根据场景选择:
方案一:用BigDecimal高精度求和
BigDecimal支持任意精度的数值计算,不会出现溢出问题。我们可以先把每个Double转成BigDecimal求和,再计算平均值后转回Double(如果结果在Double范围内):
def applyToMap(list: Map[String, List[Map[String, String]]], f: Map[String, String] => Double): Map[String, Double] = { list.mapValues { lm => // 将每个Double转换为BigDecimal val bigDecList = lm.map(f).map(BigDecimal(_)) // 求和后计算平均值,再转回Double (bigDecList.sum / bigDecList.size).toDouble } }
注意:如果最终平均值超出Double的范围,转成Double还是会得到Infinity,但这种情况比直接用Double求和溢出的概率低得多——毕竟BigDecimal没有固定的数值上限。
方案二:增量式计算平均值(推荐)
不需要先计算总和,而是通过迭代逐步更新平均值,完全避免大数值求和的溢出问题。核心公式是:新平均值 = 当前平均值 + (新数值 - 当前平均值) / 当前元素总数+1
实现代码如下,还处理了空列表的边界情况(避免除以0得到NaN):
def applyToMap(list: Map[String, List[Map[String, String]]], f: Map[String, String] => Double): Map[String, Double] = { list.mapValues { lm => val doubleList = lm.map(f) doubleList match { case Nil => 0.0 // 空列表可以自定义返回值,比如NaN或者0.0 case first :: rest => // 用foldLeft迭代更新平均值和计数 rest.foldLeft((first, 1.0)) { case ((currAvg, count), num) => val newCount = count + 1 val newAvg = currAvg + (num - currAvg) / newCount (newAvg, newCount) }._1 } } }
这种方法不仅解决了溢出问题,还节省了存储总和的内存,计算效率也更高,适合处理超长列表。
内容的提问来源于stack exchange,提问作者Sam B
相关产品推荐
相关产品推荐

