Go 中高精度浮点数与定点数的比较
Go 中高精度浮点数与定点数的比较
Go 语言中处理高精度浮点数/定点数(通常使用标准库的 math/big 包中的 big.Float / big.Rat,或者业界常用的三方库 shopspring/decimal)时,同样存在数值与精度的比较逻辑。
1. 业界常用库:shopspring/decimal(金融/电商标准)
Go 业务开发中处理金额最常用的是 [github.com/shopspring/decimal](https://github.com/shopspring/decimal),其行为与 Java 的区别最为类似:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
a, _ := decimal.NewFromString("2.0")
b, _ := decimal.NewFromString("2.00")
// 1. Equal():仅比较数值大小(等价于 Java 的 compareTo == 0)
fmt.Println(a.Equal(b)) // true
// 2. Cmp():返回 -1, 0, 1(完全等价于 Java 的 compareTo)
fmt.Println(a.Cmp(b)) // 0
// 3. Go 原生 == / reflect.DeepEqual:比较底层结构,不代表数值相等
// decimal.Decimal 的内部表示包含数值与 exp 指数等信息
// a.exp = -1, b.exp = -2
fmt.Println(a == b) // false(等价于 Java 的 equals 陷阱)
}
- 数值判断:始终使用
a.Equal(b)或a.Cmp(b) == 0。 - 避免直接
==:不要用a == b或将decimal.Decimal直接作为 Gomap的 key 进行数值比对,因为结构体内部存储的指数(exponent)不同会导致==判定为false。
2. 标准库:math/big.Float
如果使用 Go 原生标准库 math/big:
package main
import (
"fmt"
"math/big"
)
func main() {
a, _, _ := big.ParseFloat("2.0", 10, 64, big.ToNearestEven)
b, _, _ := big.ParseFloat("2.00", 10, 64, big.ToNearestEven)
// 1. Cmp():比较数值大小
// a < b 返回 -1,a == b 返回 0,a > b 返回 1
fmt.Println(a.Cmp(b)) // 0
// 2. big.Float 没有内置类似 equals 的直接方法
// 它的结构包含 prec(精度)、mode(舍入模式)和 mant(尾数切片)
// 并且指针类型不能直接通过 == 比较底层值
}
核心对比总结
| 场景 | Go(shopspring/decimal) |
Java(BigDecimal) |
行为特性 |
|---|---|---|---|
| 仅比较数值 | a.Equal(b) 或 a.Cmp(b) == 0 |
a.compareTo(b) == 0 |
推荐:2.0 与 2.00 视为相等 |
| 比较数值 + 精度/底层结构 | a == b |
a.equals(b) |
陷阱:2.0 与 2.00 视为不相等 |
| Map/Hash Key 行为 | m[a] 与 m[b] 会被视作不同 key |
HashMap 中被视作不同 key |
需先规范化(如统一保留指定小数位)再作为 key |
浙公网安备 33010602011771号