Golang中大浮点数精度下降问题探讨
Golang中大浮点数精度下降问题探讨 我正在尝试计算用于SHA512哈希的常量。但每次迭代都会丢失四个十六进制数字,我不确定原因。任何帮助或建议都将不胜感激!如果有不清楚的地方请告诉我,我会尽力解释得更清楚。
第一个常量是428a2f98d728ae22。可以通过取2的立方根来获得,提取尾数/有效数字(参考:维基百科),然后乘以16。这样就能得到本段开头十六进制数的第一个数字4。重复这个过程直到处理完前64位。
这些字代表前80个质数的立方根的小数部分的前64位。用十六进制表示,这些常量字分别为(从左到右)
参考:FIPS 180-2。
更多关于Golang中大浮点数精度下降问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html
浮点数本质上就是不精确的,如果它们不符合你的需求,你需要选择另一种数据类型。
您能否说得更具体一些?我真的不知道该怎么做。
一个能满足你需求的精确实现。
或许可以尝试一些定点数实现,并调整精度参数。
在Go语言中处理大浮点数精度下降是一个常见问题,尤其是当进行多次迭代计算时。你的代码中精度丢失主要是由于浮点数的IEEE 754标准表示限制,以及Go的math/big包在迭代过程中累积的舍入误差。我将提供一个改进的示例,使用math/big包来保持高精度计算,并生成SHA512常量。
首先,确保使用math/big.Float来设置高精度(例如,512位精度),并避免在每次迭代中重新初始化变量。以下是一个完整的Go代码示例,演示如何计算SHA512的第一个常量428a2f98d728ae22:
package main
import (
"fmt"
"math/big"
)
func main() {
// 设置高精度,例如512位,以减少精度损失
precision := uint(512)
two := big.NewFloat(2.0)
two.SetPrec(precision)
// 计算2的立方根
cubeRoot := new(big.Float).SetPrec(precision)
cubeRoot.Quo(big.NewFloat(1.0), big.NewFloat(3.0)) // 1/3 指数
cubeRoot.Exp(two, cubeRoot) // 2^(1/3)
// 提取小数部分:cubeRoot - floor(cubeRoot)
intPart := new(big.Float).SetPrec(precision)
intPart.SetInt(cubeRoot.Int(nil)) // 获取整数部分
fracPart := new(big.Float).SetPrec(precision)
fracPart.Sub(cubeRoot, intPart) // 小数部分
// 生成十六进制数字:重复乘以16并取整数部分
result := ""
for i := 0; i < 16; i++ { // 16次迭代以获取64位(每个十六进制数字4位,16*4=64位)
fracPart.Mul(fracPart, big.NewFloat(16.0))
digitInt := new(big.Int)
fracPart.Int(digitInt) // 获取整数部分作为当前数字
digit := digitInt.Int64()
if digit < 10 {
result += fmt.Sprintf("%d", digit)
} else {
result += fmt.Sprintf("%c", 'a'+(digit-10)) // 转换为a-f
}
// 更新小数部分:减去整数部分
fracPart.Sub(fracPart, new(big.Float).SetInt(digitInt))
}
fmt.Printf("计算得到的常量: %s\n", result)
// 预期输出应为类似 "428a2f98d728ae22" 的前16位十六进制数字
}
关键点说明:
- 使用高精度:通过
SetPrec(precision)设置big.Float的精度为512位,这远高于标准浮点数,有助于减少迭代中的累积误差。 - 避免重复初始化:在循环外初始化变量,并在迭代中重用它们,以防止不必要的精度损失。
- 小数部分处理:通过减去整数部分来保持小数部分的准确性,模拟浮点数尾数提取过程。
运行此代码,你应该能得到更接近428a2f98d728ae22的结果。如果仍有精度问题,可以尝试增加精度值(例如1024位)或优化计算顺序。根据FIPS 180-2标准,这种方法应能正确生成SHA512常量。


