Working with binary data in Go often requires converting byte sequences into useable numeric values. This is crucial for tasks such as reading network data, files, or data streams that transmit encoded numerical data. In this article, we’ll explore several techniques to parse numbers from bytes using Go’s binary decoding capabilities.

Understanding Bytes and Encodings

Binary data is often transmitted or stored as byte sequences. These sequences need to be parsed or decoded into numbers that your program can work with. Go offers the encoding/binary package that provides useful functions to achieve this efficiently.

Basic: Parsing Integer from a Byte Slice

Let’s start by considering a simple integer encoded in a byte slice. Suppose we have the following byte sequence representing a 16-bit unsigned integer:

package main

import (
    "encoding/binary"
    "fmt"
)

func main() {
    data := []byte{0x01, 0x02} // Little endian example for 513
    number := binary.LittleEndian.Uint16(data)
    fmt.Printf("Parsed number: %d\n", number)
}

The code above uses the binary.LittleEndian.Uint16 function to parse bytes into a uint16 integer.

Intermediate: Working with Structs

Oftentimes, binary data comes in structured formats called 'structs'. Go allows you to decode bytes into structs using predefined methods. Consider the following example where we decode a byte sequence into a custom struct:

package main

import (
    "encoding/binary"
    "fmt"
)

type NetworkPacket struct {
    ID   uint32
    Size uint16
}

func main() {
    data := []byte{0x01, 0x00, 0x00, 0x00, 0x04, 0x00} // ID = 1, Size = 4
    var packet NetworkPacket
    binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &packet)
    fmt.Printf("Parsed Packet: ID = %d, Size = %d\n", packet.ID, packet.Size)
}

The example uses binary.Read for reading binary-encoded data into Go data structures directly.

Advanced: Parsing Floating-Point Numbers

Parsing floating-point numbers follows a similar pattern but requires handling the IEEE 754 standard format more carefully. Here's an example demonstrating how to parse a float32 from bytes:

package main

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

func main() {
    data := []byte{0xdb, 0x0f, 0x49, 0x40} // Example of a float32 encoded in IEEE 754
    bits := binary.LittleEndian.Uint32(data)
    floatValue := math.Float32frombits(bits)
    fmt.Printf("Parsed floating-point number: %.3f\n", floatValue)
}

The code utilizes math.Float32frombits to convert the raw byte sequence into a float32 value directly.

Conclusion

Parsing numbers from byte sequences is an essential skill for working with binary protocols and data formats in Go. By using the encoding/binary package and mechanisms like binary.Read, developers can implement robust solutions for converting raw data into meaningful values.