首页 > 解决方案 > 1 位和 2 位小数之间的舍入差

问题描述

[Ed:重新措辞 - 很抱歉最初的措辞令人困惑。还要感谢所有建议使用整数的人,但出于几个原因,我必须使用float64.]

我有一个浮点值,如果超过 100,我想四舍五入到小数点后 1,如果超过 1000,我想四舍五入到小数点后,否则四舍五入到小数点后 2 位。这有效:

func r(f float64) float64 {
    if f >= 999.5 {
        return math.Round(f)
    }
    if f >= 99.95 {
        return math.Round(f*10) / 10
    }
    return math.Round(f*100) / 100
}

但我想知道这是否会更好:

func r(f float64) float64 {
    if f >= 999.5 {
        return math.Round(f)
    }
    if f*10 >= 999.5 {  // **** only changed line ****
        return math.Round(f*10) / 10
    }
    return math.Round(f*100) / 100
}

这更安全,因为 99.95 没有使用二进制指数的浮点精确表示。(我相信 Go 语言需要使用具有二进制指数的 IEEE fp 格式。) 999.5完全可以表示为 fp 值。

但是,我的测试(使用math.NextAfter())表明第一个解决方案对于从 99.94999999 到 99.95000001 的所有值都非常有效。第一个问题:我过度担心。

第二种解决方案的问题是我担心f*10可能会被评估两次。第二个问题:是否有任何保证优化器将确保它只执行一次。

标签: go

解决方案


如果您担心额外的乘法,而不是依赖优化器,您可以通过以下方式将其拉出:

func r(f float64) float64 {
    if f >= 999.5 {
        return math.Round(f)
    }
    
    f *= 10
    if f >= 999.5 {
        return math.Round(f) / 10
    }
    
    f *= 10
    return math.Round(f) / 100
}

推荐阅读