golang 浮点计算后转 big.Int 精度会丢失?

大数运算丢失精度

func calculate(a string, b string) *big.Int {
    v, _ := new(big.Float).SetString(a)
    decimals, _ := new(big.Float).SetString(b)
    value := new(big.Float).Mul(v, decimals)
    transVal, _ := value.Int(new(big.Int))
    return transVal
}

func main() {
    rst := calculate("0.001", "1000000000000000000")
    fmt.Println(rst) // output: 999999999999999, 精度丢失
}

期望精度不丢失

阅读 1.8k
avatarAI BotBETA
**是的,在 Go 语言中使用 `big.Float` 进行浮点计算后转换为 `big.Int` 可能会导致精度丢失**。

在你的例子中,问题出在将 `big.Float` 转换为 `big.Int` 的过程中。`big.Float` 可以表示带有小数部分的浮点数,而 `big.Int` 只能表示整数。当你调用 `value.Int(new(big.Int))` 时,`big.Float` 中的小数部分会被截断,而不是四舍五入或保留。因此,你失去了小数部分的精度。

为了解决这个问题,你可以考虑以下几种方法:

1. **提前处理小数部分**:在进行乘法运算之前,确保你的 `big.Float` 值是整数或者小数部分是你希望忽略的。

2. **四舍五入**:在转换为 `big.Int` 之前,使用 `big.Float` 的 `Round` 方法来四舍五入你的值。不过,请注意,四舍五入后仍然可能得到一个非常大的整数,这可能会超出 `big.Int` 的表示范围(尽管在大多数情况下这不是问题)。

3. **保留小数部分**:如果你需要保留小数部分的结果,那么可能不应该将其转换为 `big.Int`,而是继续使用 `big.Float`。

例如,使用四舍五入的方法可以修改你的 `calculate` 函数如下:

func calculate(a string, b string) *big.Int {

v, _ := new(big.Float).SetString(a)
decimals, _ := new(big.Float).SetString(b)
value := new(big.Float).Mul(v, decimals)
// 四舍五入到最近的整数
roundedValue, _ := value.Round(0, big.NewInt(0)).Int(new(big.Int))
return roundedValue

}


但是,请注意,即使使用四舍五入,由于浮点数的表示方式,仍然可能存在非常微小的误差。对于大多数实际应用来说,这种误差是可以接受的,但在某些需要高精度计算的场景中可能需要更加精细的处理。
2 个回答

建议使用第三方库 decimal

package main

import (
    "fmt"
    "math/big"

    "github.com/shopspring/decimal"
)

func calculate(a string, b string) *big.Int {
    v, _ := decimal.NewFromString(a)
    decimals, _ := decimal.NewFromString(b)
    value := v.Mul(decimals)
    transVal := value.BigInt()
    return transVal
}

func main() {
    rst := calculate("0.001", "1000000000000000000")
    fmt.Println(rst) // output: 1000000000000000
}

可以使用 big.Rat 来处理

package main

import (
    "fmt"
    "math/big"
)

func calculate(a string, b string) (*big.Int, error) {
    v, ok := new(big.Rat).SetString(a)
    if (!ok) {
        return nil, fmt.Errorf("invalid value for a: %s", a)
    }
    decimals, ok := new(big.Rat).SetString(b)
    if (!ok) {
        return nil, fmt.Errorf("invalid value for b: %s", b)
    }
    value := new(big.Rat).Mul(v, decimals)
    floatValue := new(big.Float).SetRat(value)
    transVal, _ := floatValue.Int(nil)
    return transVal, nil
}

func main() {
    rst, err := calculate("0.001", "1000000000000000000")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(rst) // output: 1000000000000000, 精度保持
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏