MST

星途 面试题库

面试题:Go的math包在复杂数值计算场景下的精度保持

假设你需要在Go中实现一个计算多个浮点数连乘的功能,并且要保证结果的精度尽可能高,避免因浮点数精度损失导致结果偏差过大。要求使用math包,你会如何设计这个函数?请写出具体代码,并说明你采取的控制精度的策略以及在不同量级数值相乘时可能遇到的问题和解决方法。
25.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
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
}

控制精度的策略

  1. 使用math.FMA函数math.FMA函数实现融合乘加运算,它计算a*b + c,但在一个舍入步骤中完成,而不是先计算a*b,然后将结果与c相加(这会导致两次舍入)。在连乘场景中,c为0,通过math.FMA(result, num, 0)来进行乘法运算,可以减少中间计算过程中的精度损失。

不同量级数值相乘时可能遇到的问题和解决方法

  1. 溢出问题:如果参与乘法运算的浮点数量级非常大,相乘结果可能会超出float64能够表示的范围,导致溢出,结果变为+Inf
    • 解决方法:可以通过对数变换来处理。先对每个数取自然对数,将乘法转换为加法,计算对数和后再取指数得到最终结果。例如:
func multiplyFloat64sWithLog(nums...float64) float64 {
    sumLog := 0.0
    for _, num := range nums {
        sumLog += math.Log(num)
    }
    return math.Exp(sumLog)
}
  1. 下溢问题:当参与乘法运算的浮点数量级非常小,相乘结果可能会小于float64能够表示的最小非零正数,导致下溢,结果变为0。
    • 解决方法:同样可以通过对数变换的方式,因为对数变换将乘法转换为加法,在对数域中较小的数不会下溢。另外,可以在计算过程中阶段性地检查中间结果,如果中间结果过小,可以先进行归一化处理,比如将结果乘以一个适当的常数,在最后再进行反向的缩放操作。