Go语言32位系统下高精度GPS计算的执行时长优化
注意!
最初的问题源于我对32位系统上64位类型的误解。原来我的代码出错是因为将本该用float64的值误用为float32,这让我误以为32位系统无法使用float64,评论区的@BurakSerdar和@Adrian很快纠正了这个问题。为了不使评论失效,我保留了下方的原始问题。
重构后的可行方案
我重构了代码,得到两种可行方案:一种基于@PakUula的回答,采用int64与字符串操作实现;另一种采用float64实现。float64方案的执行时长约高25%,但代码可读性更强。
基准测试结果(树莓派3上运行)
BenchmarkAll/int64Method-4 286648 3977 ns/op 32 B/op 3 allocs/op BenchmarkAll/float64Method-4 242878 5000 ns/op 24 B/op 2 allocs/op
Float64 实现方法
func float64Method(msg []byte) string { payload := msg[6:] latFloat := float64(int32(binary.LittleEndian.Uint32(payload[8:12]))) precFloat := float64(int8(payload[24])) / 100 latSum := (latFloat + precFloat) / 1e7 return fmt.Sprintf("%2.9f", latSum) }
int64 实现方法
func int64Method(msg []byte) string { payload := msg[6:] rawLatUint32 := binary.LittleEndian.Uint32(payload[8:12]) rawLatInt32 := int32(rawLatUint32) rawLatInt64 := int64(rawLatInt32) fixLat8 := int8(payload[24]) highPrecLat := rawLatInt64*100 + int64(fixLat8) negative := (highPrecLat < 0) sign := "" if negative { sign = "-" highPrecLat = -highPrecLat } latInt := highPrecLat / precision latFrac := highPrecLat % precision return fmt.Sprintf("%s%d.%09d", sign, latInt, latFrac) }
我曾寻找可直接将4字节小端值转换为int32的方法,但内置包中似乎没有该功能。
原始问题
我正在使用树莓派3(32位)与Ublox M8 GPS接收器开发地形测量系统,代码采用Go语言编写(版本Go 1.19.2)。
Ublox接收器会生成包含高精度GPS位置的字节消息,纬度和经度值分别由1个4字节有符号整数(I4)和1个1字节有符号整数(I1)组成,字节序为LittleEndian。
高精度坐标的计算方式为:Degrees * 1e-7 = I4 + (I1 * 1e-2),最终需显示为带9位小数的度数,例如:Lat: 48.944665243°, Lon: -13.117730989°
由于我对Go语言(及数学计算)的知识有限,遇到了问题:32位系统下内置数据类型无法处理此类精度的运算,因此我使用math/big包来获取正确结果,但math/big的运算耗时极高——在64位系统中使用内置类型和字符串格式化可实现需求,而使用math/big后处理时长变为原来的4倍。
我寻求改进这段代码的建议:是否有无需使用math/big的更佳实现方式?同时我也不确定自己对math/big的使用是否正确。
latFloat := big.NewFloat(float64(int32(binary.LittleEndian.Uint32(payload[12:16])))) precFloat := big.NewFloat(float64(int8(payload[25])) / 100) latSum := big.NewFloat(0.0) latSum.Add(latSum, latFloat) latSum.Add(latSum, precFloat) multip := big.NewFloat(0.0000001) latSum.Mul(latSum, multip) h.Lat = fmt.Sprint(latSum.Text('f', 9))
此外,我对binary.LittleEndian解码也有疑问:是否存在无需通过binary.LittleEndian.Uint32方法,直接将4字节小端值转换为int32的方式?当前的转换步骤显得多余。
内容的提问来源于stack exchange,提问作者authoritah

