Note!最初的问题是因为我在32位系统上被64位类型搞混了。原来我的原始代码失败了,因为我使用了float32作为一个本应是float64的值。这个错误使我认为我不能在32位系统上使用float64。这在评论中很快就被纠正了。感谢“BurakSerdar”和“禤浩焯”。为了不使评论无效,我在下面留下了原来的问题。
我已经重构了我的代码,我有两个可行的选择。一个是基于@PakUula发布的答案,使用int64和字符串操作。另一种是使用float64。float64解决方案的执行时间增加了25%,但代码似乎更容易阅读。
在Raspberry pi3上运行基准测试为这两种方法提供了以下结果:
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/opFloat64方法
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)
}围棋操场上的工作示例:https://go.dev/play/p/cQo67gMDwGS
我正在寻找一种方法,将4字节的小endian值直接转换为int32。它似乎不存在于内置包中。
原始我正在创建一个地形测量系统使用一个覆盆子Pi3 (32位)和一个Ublox M8 GPS接收器。代码将用Go写成。(走1.19.2)
Ublox接收机在字节消息中生成高精度GPS位置。纬度和经度值分别由一个4字节有符号整数(I4)和一个1字节有符号整数(I1)组成。字节值为LittleEndian。
通过添加I4和I1将高精度坐标组合在一起:度数* 1e-7 = I4 + (I1 * 1e-2)
拉长位置将显示为9小数位数的度,例如: Lat: 48.944665243°,Lon:-13.117730989°
这就是我遇到问题的地方,因为我对围棋(和数学)的知识有限。没有一个内置的数据类型能够用这个大小的数字进行算术(我的系统是32位),所以我使用数学/big来获得正确的结果。然而,数学/大似乎需要很大的处理能力。我在64位系统上运行了代码,可以使用内置的类型和字符串格式来获得正确的结果。使用数学/大导致处理时间翻两番。
我正在寻找关于如何改进这段代码的建议。我不知道是否有更好的方法来做到这一点而不用数学/大。另外,我真的不知道我对数学/大字号的使用是否正确。
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))Go游乐场中有一个可用的示例:https://go.dev/play/p/EAN4YmqeuwO
此外,我想知道binary.LittleEndian解码。是否有一种无需使用Uint32方法binary.LittleEndian来完成此操作的方法。似乎没有必要通过Uint32到int32来获得正确的值。
发布于 2022-10-26 01:24:17
为什么不使用int64呢?Go运行时在32位平台上非常有效地支持它。
返工
下面是一个例子:https://go.dev/play/p/XRVKaGvNMrw
const precisionFactor = 1_000_000_000
// Convert fixed-precision int to a string
func IntStringWithPrecision(val int64) string {
negative := (val < 0)
sign := ""
if negative {
sign = "-"
val = -val
}
deg := val / precisionFactor
frac := val % precisionFactor
return fmt.Sprintf("%s%d.%09d", sign, deg, frac)
}
// Convert a float to a string with 9 digits after dot
func FloatStringWithPrecision(val float64) string {
return fmt.Sprintf("%0.9f", val)
}
// Parse payload as a fixed-precision int
func GetLatAsInt(payload []byte) int64 {
rawLatUint32 := binary.LittleEndian.Uint32(payload[12:16])
rawLatInt32 := int32(rawLatUint32)
rawLatInt64 := int64(rawLatInt32)
fixLat8 := int8(payload[25])
return rawLatInt64*100 + int64(fixLat8)
}
// Parse payload as a float
func GetLatAsFloat(payload []byte) float64 {
latFloat := float64(int32(binary.LittleEndian.Uint32(payload[12:16])))
precFloat := float64(int8(payload[25])) / 100
return (latFloat + precFloat) / 1e7
}诀窍是,我们将9位数的精度保持在一个整数内,而拆分成整数和小数部分,通过与1e9等价的整数进行1e9运算。
产出如下:
test(305419896, -112)
Using integer: 30.541989488
Using float: 30.541989488
test(100, -56)
Using integer: 0.000009944
Using float: 0.000009944
test(1, 2)
Using integer: 0.000000102
Using float: 0.000000102
test(-1, 2)
Using integer: -0.000000098
Using float: -0.000000098
test(-10, 20)
Using integer: -0.000000980
Using float: -0.000000980
test(-305419896, 9)
Using integer: -30.541989591
Using float: -30.541989591如您所见,结果是相同的。
基准:https://go.dev/play/p/zdeqTQuDxmS
goos: linux
goarch: amd64
pkg: example.org
cpu: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
BenchmarkInt-8 985913821 1.219 ns/op 0 B/op 0 allocs/op
BenchmarkFloat-8 616161028 1.990 ns/op 0 B/op 0 allocs/op
BenchmarkToStringInt-8 6727627 175.8 ns/op 24 B/op 2 allocs/op
BenchmarkToStringFloat-8 4459144 269.1 ns/op 24 B/op 2 allocs/op
BenchmarkToStringNegativeInt-8 5851466 204.7 ns/op 40 B/op 3 allocs/op
BenchmarkToStringNegativeFloat-8 4456899 271.2 ns/op 24 B/op 2 allocs/opint64版本比有浮点数的版本快40%,而ToString用于int64的速度更快。
当然,我的CPU是64位,在流水线上支持浮点,在32位的整数CPU上,结果会有所不同。尽管如此,int的性能仍应优于浮点版本。
https://stackoverflow.com/questions/74199735
复制相似问题