🔟

Endianness

CategorySystem

Overview

Endianness is the sequential order in which bytes are arranged when stored in memory.

Bytes are represented in big-endian or little-endian format.

❤️
Modern platforms mostly use the little-endian representation.

Bit Endianness

Bit endianness is the convention used to identify the bit positions in a binary number.

Least Significant Byte

The least significant byte (LSB) is the byte containing the least significant bit.

The binary representation of 149, with the LSB highlighted.

Most Significant Byte

The most significant byte (MSB) is the byte containing the most significant bit.

The unsigned binary representation of 149, with the MSB highlighted.

Byte Endianness

Big-endian

Bytes are ordered from the most significant bit to the least significant bit.

The most significant bit is stored first and the least significant bit is stored last.

Little-endian

Bytes are ordered from the least significant bit to the most significant bit.

The least significant bit is stored first and the most significant bit is stored last.

Bi-endianness

Some architectures feature a setting which allows for switchable endianness.

Hardware

NameEndianness
ARMbi-endian
Intel x86little-endian
Intel x86-64little-endian
iOSlittle-endian
PlayStation 3big-endian
PlayStation 4little-endian
PlayStation 5little-endian
Switchlittle-endian
Wiibig-endian
Wii Ubig-endian
Xbox 360big-endian
Xbox Onelittle-endian
Xbox Scarlettlittle-endian

Byte Swap

When a binary file created on a platform is read on another platform with a different endianness, byte swapping is required.

Byte swap depends on the size of the numbers (ie. a 16-bit value and a 32-bit value require a different byte swap).

The data format must therefore be known to perform endianness conversion.

Implementations

16-bit integer: swap 2 bytes.

uint16_t Swap(uint16_t value)
{
    uint16_t result = 0;
    result |= (value & 0x00FF) << 8;
    result |= (value & 0xFF00) >> 8;
    return result;
}

32-bit integer: swap 4 bytes.

uint32_t Swap(uint32_t value)
{
    uint32_t result = 0;
    result |= (value & 0x000000FF) << 24;
    result |= (value & 0x0000FF00) << 8;
    result |= (value & 0x00FF0000) >> 8;
    result |= (value & 0xFF000000) >> 24;
    return result;
}

32-bit floating point:

This method is not recommended because some compiler performs optimizations than cause invalid results when doing a reinterpret_cast between floating point and integers values.
float Swap(float value)
{
    uint32_t result = Swap(reinterpret_cast<uint32_t>(value));
    return reinterpret_cast<float>(result);
}
❤️
This method is recommended as unions are portable on different compilers.
float Swap(float value)
{
    union
    {
        float f;
        uint32_t u;
    } swap;

    swap.f = x;
    swap.u = Swap(swapper.u);
    return swap.f;
}

Strategies

There are two strategies when generating binary data.

References