首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >改进Go中高精度计算的执行时间

改进Go中高精度计算的执行时间
EN

Stack Overflow用户
提问于 2022-10-25 20:19:02
回答 1查看 64关注 0票数 -1

Note!最初的问题是因为我在32位系统上被64位类型搞混了。原来我的原始代码失败了,因为我使用了float32作为一个本应是float64的值。这个错误使我认为我不能在32位系统上使用float64。这在评论中很快就被纠正了。感谢“BurakSerdar”和“禤浩焯”。为了不使评论无效,我在下面留下了原来的问题。

我已经重构了我的代码,我有两个可行的选择。一个是基于@PakUula发布的答案,使用int64和字符串操作。另一种是使用float64。float64解决方案的执行时间增加了25%,但代码似乎更容易阅读。

在Raspberry pi3上运行基准测试为这两种方法提供了以下结果:

代码语言:javascript
复制
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方法

代码语言:javascript
复制
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方法

代码语言:javascript
复制
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位系统上运行了代码,可以使用内置的类型和字符串格式来获得正确的结果。使用数学/大导致处理时间翻两番。

我正在寻找关于如何改进这段代码的建议。我不知道是否有更好的方法来做到这一点而不用数学/大。另外,我真的不知道我对数学/大字号的使用是否正确。

代码语言:javascript
复制
    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来获得正确的值。

EN

回答 1

Stack Overflow用户

发布于 2022-10-26 01:24:17

为什么不使用int64呢?Go运行时在32位平台上非常有效地支持它。

返工

下面是一个例子:https://go.dev/play/p/XRVKaGvNMrw

代码语言:javascript
复制
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运算。

产出如下:

代码语言:javascript
复制
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

代码语言:javascript
复制
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/op

int64版本比有浮点数的版本快40%,而ToString用于int64的速度更快。

当然,我的CPU是64位,在流水线上支持浮点,在32位的整数CPU上,结果会有所不同。尽管如此,int的性能仍应优于浮点版本。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74199735

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档