go 的 `uint64` 类型无法精确表示超过 2^64−1 的整数,而 19 位十进制数(如 10736581604118680000)在转换为 `uint64` 时会因浮点近似或截断导致低 4 位偏差;应使用 `math/big.int` 或确保输入为精确字符串而非浮点近似值。
在物联网设备对接 1-Wire iButton 等硬件时,常需
将厂商刻印的十六进制 ID(如 000015877CD0)与设备读取的十进制数值进行双向验证。但当十进制数达到 19 位(如 10736581604118680000)时,直接赋值给 Go 的 uint64 变量会引发静默精度丢失——这不是 Go 的 Bug,而是底层数值表示的必然限制。
根本原因在于:
✅ 正确做法:绕过 float64 和 uint64 的中间表示,全程使用精确整数运算。
package main
import (
"fmt"
"math/big"
"strconv"
)
func main() {
// ✅ 正确:从十六进制字符串直接解析(无精度损失)
hexStr := "95000015877CD001" // 厂商刻印完整值
bigInt := new(big.Int)
if _, ok := bigInt.SetString(hexStr, 16); !ok {
panic("invalid hex string")
}
// 提取中间 12 位(跳过前 2 字节 + 后 2 字节)
resultHex := fmt.Sprintf("%016X", bigInt)[2:14]
fmt.Println("Extracted ID:", resultHex) // 输出:000015877CD0
// ✅ 或从十进制字符串安全解析(若必须用 dec 输入)
decStr := "10736581604118679553" // 注意:这是精确整数,非 80000 结尾的近似值
if _, ok := bigInt.SetString(decStr, 10); !ok {
panic("invalid decimal string")
}
resultHex = fmt.Sprintf("%016X", bigInt)[2:14]
fmt.Println("From exact decimal:", resultHex) // 同样输出:000015877CD0
}// ❌ 危险:直接写 uint64 字面量(Go 会尝试解析为 float64 再转 uint64)
var V = 10736581604118680000 // 实际被当作 float64 近似,已失真
// ❌ 更危险:先 ParseFloat 再转 uint64
f, _ := strconv.ParseFloat("10736581604118680000", 64)
v := uint64(f) // 此步发生不可逆精度截断
// ❌ 错误假设:认为 uint64 能无损容纳所有 19 位十进制数
// 实际上:10736581604118679553 和 10736581604118680000 在 uint64 中可能映射到同一值// 比较 float64 解析前后是否相等
s := "10736581604118679553"
f, _ := strconv.ParseFloat(s, 64)
back := strconv.FormatUint(uint64(f), 10)
fmt.Println("Original:", s)
fmt.Println("Rounded: ", back) // 若不等,则已失真
// 输出:Original: 10736581604118679553
// Rounded: 10736581604118679552 ← 已偏差!精度不是玄学,而是可预测的工程约束。用对工具,就能让每颗 iButton 的身份认证坚如磐石。