package main
import (
"fmt"
"math"
)
func multiplyFloat64s(nums...float64) float64 {
result := 1.0
for _, num := range nums {
result = math.FMA(result, num, 0)
}
return result
}
控制精度的策略
- 使用
math.FMA
函数:math.FMA
函数实现融合乘加运算,它计算a*b + c
,但在一个舍入步骤中完成,而不是先计算a*b
,然后将结果与c
相加(这会导致两次舍入)。在连乘场景中,c
为0,通过math.FMA(result, num, 0)
来进行乘法运算,可以减少中间计算过程中的精度损失。
不同量级数值相乘时可能遇到的问题和解决方法
- 溢出问题:如果参与乘法运算的浮点数量级非常大,相乘结果可能会超出
float64
能够表示的范围,导致溢出,结果变为+Inf
。
- 解决方法:可以通过对数变换来处理。先对每个数取自然对数,将乘法转换为加法,计算对数和后再取指数得到最终结果。例如:
func multiplyFloat64sWithLog(nums...float64) float64 {
sumLog := 0.0
for _, num := range nums {
sumLog += math.Log(num)
}
return math.Exp(sumLog)
}
- 下溢问题:当参与乘法运算的浮点数量级非常小,相乘结果可能会小于
float64
能够表示的最小非零正数,导致下溢,结果变为0。
- 解决方法:同样可以通过对数变换的方式,因为对数变换将乘法转换为加法,在对数域中较小的数不会下溢。另外,可以在计算过程中阶段性地检查中间结果,如果中间结果过小,可以先进行归一化处理,比如将结果乘以一个适当的常数,在最后再进行反向的缩放操作。