GolangNote

Golang笔记

Golang Float Slice 与 Byte Slice 互转

Permalink

在 Go 中,将 []float64[]byte 相互转换需要特别注意内存布局和字节序问题。以下是几种实现方法:

方法一:使用 encoding/binary 包(推荐)

Float Slice 转 Byte Slice

Go:
1
2
3
4
5
6
7
8
9
10
11
12
import (
    "encoding/binary"
    "math"
)

func Float64SliceToBytes(floats []float64) []byte {
    buf := make([]byte, 8*len(floats))
    for i, f := range floats {
        binary.LittleEndian.PutUint64(buf[i*8:], math.Float64bits(f))
    }
    return buf
}

Byte Slice 转 Float Slice

Go:
1
2
3
4
5
6
7
func BytesToFloat64Slice(bytes []byte) []float64 {
    floats := make([]float64, len(bytes)/8)
    for i := range floats {
        floats[i] = math.Float64frombits(binary.LittleEndian.Uint64(bytes[i*8:]))
    }
    return floats
}

方法二:使用 unsafe 包(高性能但不安全)

Go:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import (
    "reflect"
    "unsafe"
)

func Float64SliceToBytesUnsafe(floats []float64) []byte {
    header := *(*reflect.SliceHeader)(unsafe.Pointer(&floats))
    header.Len *= 8
    header.Cap *= 8
    return *(*[]byte)(unsafe.Pointer(&header))
}

func BytesToFloat64SliceUnsafe(bytes []byte) []float64 {
    header := *(*reflect.SliceHeader)(unsafe.Pointer(&bytes))
    header.Len /= 8
    header.Cap /= 8
    return *(*[]float64)(unsafe.Pointer(&header))
}

警告:unsafe 方法虽然性能高,但会绕过类型安全检查,使用时需要确保字节对齐和长度正确。

方法三:使用 bytes.Buffer 和 binary.Write/Read

Go:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import (
    "bytes"
    "encoding/binary"
)

func Float64SliceToBytesBuffer(floats []float64) ([]byte, error) {
    buf := new(bytes.Buffer)
    err := binary.Write(buf, binary.LittleEndian, floats)
    if err != nil {
        return nil, err
    }
    return buf.Bytes(), nil
}

func BytesToFloat64SliceBuffer(bytes []byte) ([]float64, error) {
    buf := bytes.NewReader(bytes)
    floats := make([]float64, len(bytes)/8)
    err := binary.Read(buf, binary.LittleEndian, &floats)
    if err != nil {
        return nil, err
    }
    return floats, nil
}

完整示例

Go:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

import (
    "encoding/binary"
    "fmt"
    "math"
)

func main() {
    // 原始float slice
    floats := []float64{3.14159, 2.71828, 1.61803, 0.57721}
    
    // 转换为byte slice
    bytes := Float64SliceToBytes(floats)
    fmt.Printf("Floats: %v\nBytes: %v\n", floats, bytes)
    
    // 转换回float slice
    newFloats := BytesToFloat64Slice(bytes)
    fmt.Printf("Converted back: %v\n", newFloats)
    
    // 验证是否一致
    fmt.Println("Equal:", floatsEqual(floats, newFloats))
}

func Float64SliceToBytes(floats []float64) []byte {
    buf := make([]byte, 8*len(floats))
    for i, f := range floats {
        binary.LittleEndian.PutUint64(buf[i*8:], math.Float64bits(f))
    }
    return buf
}

func BytesToFloat64Slice(bytes []byte) []float64 {
    floats := make([]float64, len(bytes)/8)
    for i := range floats {
        floats[i] = math.Float64frombits(binary.LittleEndian.Uint64(bytes[i*8:]))
    }
    return floats
}

func floatsEqual(a, b []float64) bool {
    if len(a) != len(b) {
        return false
    }
    for i := range a {
        if a[i] != b[i] {
            return false
        }
    }
    return true
}

性能比较

  1. encoding/binary 方法:

    • 安全可靠
    • 适用于所有平台
    • 性能中等
  2. unsafe 方法:

    • 性能最高(零拷贝)
    • 不安全,需要确保字节对齐
    • 平台依赖性风险
  3. bytes.Buffer 方法:

    • 使用简单
    • 性能较低(因为有额外的缓冲区操作)
    • 适合小数据量或IO操作

注意事项

  1. 字节序问题:不同系统可能使用不同字节序(大端/小端),需要统一
  2. 内存对齐:确保byte slice长度是8的倍数(float64占8字节)
  3. 浮点数精度:转换过程不会损失精度
  4. 安全性:unsafe方法需要谨慎使用

选择哪种方法取决于您的具体需求:安全性优先选择encoding/binary,性能优先选择unsafe,简单性优先选择bytes.Buffer

本文网址: https://golangnote.com/topic/323.html 转摘请注明来源

Related articles

Golang sync.WaitGroup 的 Wait 超时处理

sync.WaitGroup 使用 `Add(1)`、`Done()`、`Wait()`组合来实现多协程等待,如果某一协程未能合理处理错误,导致无法退出,此时需要引入超时机制。下面是一种超时处理方法。...

Write a Comment to "Golang Float Slice 与 Byte Slice 互转"

Submit Comment Login
Based on Golang + fastHTTP + sdb | go1.22.3 Processed in 1ms