ElearningWorld.org

For the online learning world

Elearning WorldLinuxTechnical

Endianness

Endianness is the order of the bytes in a computer system when representing a number that requires more than one byte to store it. In essence with positive numbers, greater than 255 and with storing either positive or negative, greater than -128 or 127. But why does that matter to me?

Disclaimers

Names / logos can be trademarks of their respective owners. Please review their website for details. I am independent from the organisations mentioned and am in no way writing for or endorsed by them. The information presented in this article is written according to my own understanding, there could be inaccuracies, so please do undertake your own research. The featured image is my copyright, please don’t use without my permission.

Representation

In reality to most people ‘endianness’ doesn’t matter. Software just works, or doesn’t. You don’t need to know the why on both accounts, you just want the former and not the latter. But here is a thing, what if we take that aside and just understand because we want to discover something new?

All data in a computer is stored as bytes. Bytes are made up of bits, switches that can be on or off, binary. Eight bit make up a byte, four bits make a nibble. Two nibbles in every byte. The bits represent the placement value of a written number. Decimal has 1’s, 10’s, 100’s columns and so forth. Binary has 1’s. 2’s, 4’s columns and so on. The powers of two rather than the powers of ten. See my post ‘What is a Byte anyway?’.

A computer fundamentally only understands bytes and their values as a number and can compare the numbers. It does not understand that they can have a meaning, a representation. It is us that programs them to represent our world. The character we have used represented by a value in a single byte is one example of this. Of course there is ‘UTF-8‘ that uses one to four bytes to allow representation of more characters beyond what ‘ASCII‘ can provide. This is great for when we wish to convey our languages, but what if we’re conveying just numbers for example? Then the number ‘100’ typed as digits, characters, becomes three bytes when we know that a single byte can represent it as a value.

But what about the number 404? Ignoring the ‘Sign bit’ and dealing with only positive numbers, then we need two bytes, sixteen bits, where the sixteenth bit represents the 65536’s column. Therefore 404 is ‘0000 0001 1001 0100’. Notice that the most significant byte (the one with a bit set for the 256’s column) is first, followed by the least significant byte. This arrangement is called ‘Big endian’ and follows how left to right languages like English are read. If the bytes were swapped then this is a called ‘Little endian’ with the least significant byte first. How the computer, the CPU decides which one it uses or both is down to the architecture. There is lots more on ‘wikipedia.org/wiki/Endianness’. Within a computer the endianness only really matters if the data is destined to be at some point read by another computer with a different endianness. This can be true of stored data and data that is sent over a network. The former could be ‘UTF-16’ where it has a means of saying the byte order, the endianness with a ‘Byte Order Mark’, a ‘BOM’. Here I’m going to demonstrate conceptually about over a ‘network’, specifically the ‘Internet protocol suite’, where the endianness is ‘big’.

A demonstration

Time is something we all tend to be concerned about. The when of things, be it past, present or future. Storing time like anything else needs to be done as a number on a computer no matter how it is in the real world. One way of doing this is ‘Unix Time’ which now stores the number of seconds since the 00:00:00 on the 1st January 1970 as a 64 bit value, or 8 bytes. It was a 32 bit value, 4 bytes but that has the ‘Year 2038 problem’, another form of the ‘Year 2000 problem’ or ‘Y2K’.

Now we want one device to tell another the time an event happened. Therefore to adhere to the ‘Internet protocol suite’, we need to convert the time value from little to big endian if we are a little endian device. In this example I’m using a ‘Raspberry Pi B+‘ which is using little endian. There are functions (wikipedia.org/wiki/Endianness#Networking) that already do this for us, such as ‘htonl’ and ‘htonll’, and back ‘ntohl’ and ‘ntohll’ – where ‘h’ is ‘host’, ‘n’ is network and ‘l’ stands for ‘long’ – typically a 32 bit integer. But that would defeat understanding what is going on internally, as would use of the ‘byteswap’ function. Therefore I have adapted some functions from ‘byteswap-c’ that uses the bitwise shift ‘<<’ and ‘>>’ operators (www.geeksforgeeks.org/cpp/left-shift-right-shift-operators-c-cpp) to shift the bits of the bytes to in effect move the bytes within the given data type (uint32_t / uint64_t) with the bitwise ‘And’ – ‘&’ and ‘Or’ – ‘|’ operators (www.geeksforgeeks.org/cpp/cpp-bitwise-operators) to produce the result.

The code is, ‘time.cpp’:

/**
 * @file time.cpp
 * @copyright (c) 2026 G J Barnard.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
 * conditions are met:
 *  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials provided with the distribution.
 *  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @par License
 * SPDX-License-Identifier: BSD-3-Clause - https://spdx.org/licenses/BSD-3-Clause.html.
 *
 * @version V1.0.0
 */

#include <cstdint>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <vector>

using namespace std;

// Byte swapping functions, adapted from: https://github.com/krxecs/byteswap-c.
// License: 0-clause BSD License (SPDX license code: `0BSD`): https://spdx.org/licenses/0BSD.html.
/**
 * @brief Swap the byte order of a 32-bit unsigned integer.
 * @param num The 32-bit unsigned integer to swap.
 * @return The byte-swapped 32-bit unsigned integer.
 */
uint32_t bswap32(uint32_t num) {
    int nums = sizeof(num);
    vector<uint8_t> numbytesp(reinterpret_cast<uint8_t*>(&num), reinterpret_cast<uint8_t*>(&num) + nums);
    cout << "bswap32 parameter dec value: " << dec << num << endl;
    cout << "bswap32 parameter hex value: " << hex << setw(8) << setfill('0') << num << endl;
    cout << "bswap32 parameter in memory: ";
    for (uint8_t i = 0; i < nums; i++) {
        cout << hex << setw(2) << setfill('0') << (int) numbytesp.at(i);
    }
    cout << endl;

    // Note: Changed flipping of middle pair to be same as bswap64.
    uint32_t bshifted[4];
    bshifted[0] = (num >> 24);
    bshifted[1] = (num >> 8);
    bshifted[2] = (num << 8);
    bshifted[3] = (num << 24);

    for (uint8_t i = 0; i < 4; i++) {
        cout << "byte " << dec << (int) i << " shifted: " << hex << setw(8) << setfill('0') << bshifted[i] << endl;
    }

    uint32_t banded[4];
    banded[0] = bshifted[0] & 0xff;
    banded[1] = bshifted[1] & 0xff00;
    banded[2] = bshifted[2] & 0xff0000;
    banded[3] = bshifted[3] & 0xff000000;

    for (uint8_t i = 0; i < 4; i++) {
        cout << "byte " << dec << (int) i << " bitwise anded: " << hex << setw(8) << setfill('0') << banded[i] << endl;
    }

    uint32_t result = banded[0] | banded[1] | banded[2] | banded[3];

    /*
    return ((num >> 24) & 0xff) |    // move byte 3 to byte 0
         ((num << 8) & 0xff0000) |   // move byte 1 to byte 2
         ((num >> 8) & 0xff00) |     // move byte 2 to byte 1
         ((num << 24) & 0xff000000); // byte 0 to byte 3
    */

    vector<uint8_t> numbytesr(reinterpret_cast<uint8_t*>(&result), reinterpret_cast<uint8_t*>(&result) + nums);
    cout << "bswap32 all bytes swapped result dec value: " << dec << result << endl;
    cout << "bswap32 all bytes swapped result hex value: " << hex << setw(8) << setfill('0') << result << endl;
    cout << "bswap32 all bytes swapped result in memory: ";
    for (uint8_t i = 0; i < nums; i++) {
        cout << hex << setw(2) << setfill('0') << (int) numbytesr.at(i);
    }
    cout << endl;

    return result;
}

/**
 * @brief Swap the byte order of a 64-bit unsigned integer.
 * @param x The 64-bit unsigned integer to swap.
 * @return The byte-swapped 64-bit unsigned integer.
 */
uint64_t bswap64(uint64_t num) {
    int nums = sizeof(num);
    vector<uint8_t> numbytesp(reinterpret_cast<uint8_t*>(&num), reinterpret_cast<uint8_t*>(&num) + nums);
    cout << "bswap64 parameter dec value: " << dec << num << endl;
    cout << "bswap64 parameter hex value: " << hex << setw(16) << setfill('0') << num << endl;
    cout << "bswap64 parameter in memory: ";
    for (uint8_t i = 0; i < nums; i++) {
        cout << hex << setw(2) << setfill('0') << (int) numbytesp.at(i);
    }
    cout << endl;

    uint64_t bshifted[8];
    bshifted[0] = num >> 56;
    bshifted[1] = num >> 40;
    bshifted[2] = num >> 24;
    bshifted[3] = num >> 8;
    bshifted[4] = num << 8;
    bshifted[5] = num << 24;
    bshifted[6] = num << 40;
    bshifted[7] = num << 56;

    for (uint8_t i = 0; i < 8; i++) {
        cout << "byte " << dec << (int) i << " shifted: " << hex << setw(16) << setfill('0') << bshifted[i] << endl;
    }

    uint64_t banded[8];
    banded[0] = bshifted[0] & 0xff;
    banded[1] = bshifted[1] & 0xff00;
    banded[2] = bshifted[2] & 0xff0000;
    banded[3] = bshifted[3] & 0xff000000;
    banded[4] = bshifted[4] & 0xff00000000;
    banded[5] = bshifted[5] & 0xff0000000000;
    banded[6] = bshifted[6] & 0xff000000000000;
    banded[7] = bshifted[7] & 0xff00000000000000;

    for (uint8_t i = 0; i < 8; i++) {
        cout << "byte " << dec << (int) i << " bitwise anded: " << hex << setw(16) << setfill('0') << banded[i] << endl;
    }

    uint64_t result =
        banded[0] | banded[1] | banded[2] | banded[3] |
        banded[4] | banded[5] | banded[6] | banded[7];

    /*
    return x >> 56                        // move byte 7 to byte 0
         | ((x >> 40) & 0xff00)           // move byte 6 to byte 1
         | ((x >> 24) & 0xff0000)         // move byte 5 to byte 2
         | ((x >> 8) & 0xff000000)        // move byte 4 to byte 3
         | ((x << 8) & 0xff00000000)      // move byte 3 to byte 4
         | ((x << 24) & 0xff0000000000)   // move byte 2 to byte 5
         | ((x << 40) & 0xff000000000000) // move byte 1 to byte 6
         | x << 56;                       // move byte 0 to byte 7
    */

    vector<uint8_t> numbytesr(reinterpret_cast<uint8_t*>(&result), reinterpret_cast<uint8_t*>(&result) + nums);
    cout << "bswap64 all bytes swapped result dec value: " << dec << result << endl;
    cout << "bswap64 all bytes swapped result hex value: " << hex << setw(16) << setfill('0') << result << endl;
    cout << "bswap64 all bytes swapped result in memory: ";
    for (uint8_t i = 0; i < nums; i++) {
        cout << hex << setw(2) << setfill('0') << (int) numbytesr.at(i);
    }
    cout << endl;

    return result;
}

/**
 * Sending and receiving time over a conceptual communications channel.
 */
int main() {
    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        cout << "Little endian on: ";
    #else
        cout << "Big endian on: ";
    #endif

    #ifdef __linux__
        cout << "Linux." << endl;
    #elif _WIN32
        cout << "Windows." << endl;
    #else
        cout << "Not Linux or Windows." << endl;
    #endif

    cout << "Current time is: ";

    time_t timestamp;
    time(&timestamp);

    cout << ctime(&timestamp);

    int ts = sizeof(timestamp);
    cout << "timestamp decimal and size: " << timestamp << " : " << ts << endl;
    vector<uint8_t> timestampbytes(reinterpret_cast<uint8_t*>(&timestamp), reinterpret_cast<uint8_t*>(&timestamp) + ts);
    cout << "timestamp hex value: " << hex << timestamp << endl;
    cout << "timestamp in memory: ";
    for (uint8_t i = 0; i < ts; i++) {
        cout << hex << setw(2) << setfill('0') << (int) timestampbytes.at(i);
    }
    cout << endl;

    // Convert to network byte order for our endianness.

    uint32_t tosend32 = timestamp;
    uint64_t tosend64 = timestamp;
    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        tosend32 = bswap32(tosend32);
        tosend64 = bswap64(tosend64);
    #endif

    int tss32 = sizeof(tosend32);
    vector<uint8_t> tosend32bytes(reinterpret_cast<uint8_t*>(&tosend32), reinterpret_cast<uint8_t*>(&tosend32) + tss32);

    cout << "Sending: " << endl << "32: ";
    for (uint8_t i = 0; i < tss32; i++) {
        cout << hex << setw(2) << setfill('0') << (int) tosend32bytes.at(i);
    }
    cout << endl << "64: ";

    int tss64 = sizeof(tosend64);
    vector<uint8_t> tosend64bytes(reinterpret_cast<uint8_t*>(&tosend64), reinterpret_cast<uint8_t*>(&tosend64) + tss64);

    for (uint8_t i = 0; i < tss64; i++) {
        cout << hex << setw(2) << setfill('0') << (int) tosend64bytes.at(i);
    }
    cout << endl;

    // Convert back from network byte order to our endianness.
    uint8_t *fromsend32n8ar = tosend32bytes.data();
    uint8_t *fromsend64n8ar = tosend64bytes.data();

    cout << "Received: " << endl << "32: ";
    for (uint8_t i = 0; i < tss32; i++) {
        cout << hex << setw(2) << setfill('0') << (int) fromsend32n8ar[i];
    }
    cout << endl << "64: ";
    for (uint8_t i = 0; i < tss64; i++) {
        cout << hex << setw(2) << setfill('0') << (int) fromsend64n8ar[i];
    }
    cout << endl;

    uint32_t *fromsend32nptr = reinterpret_cast<uint32_t *>(fromsend32n8ar);
    uint32_t fromsend32n = *fromsend32nptr;
    uint64_t *fromsend64nptr = reinterpret_cast<uint64_t *>(fromsend64n8ar);
    uint64_t fromsend64n = *fromsend64nptr;
    #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        uint32_t fromsend32h = bswap32(fromsend32n);
        uint64_t fromsend64h = bswap64(fromsend64n);
    #endif
    cout << "Received 32 time value: " << dec << fromsend32h << " : " << hex << fromsend32h << endl;
    cout << "Received 64 time value: " << dec << fromsend64h << " : " << hex << fromsend64h << endl;

    cout << "Received 32 time is: ";
    uint64_t rect32t = fromsend32h;
    time_t *r32time = reinterpret_cast<time_t*>(&rect32t);
    cout << ctime(r32time);

    cout << "Received 64 time is: ";
    uint64_t rect64t = fromsend64h;
    time_t *r64time = reinterpret_cast<time_t*>(&rect64t);

    cout << ctime(r64time);

    return 0;
}

You can also get the code from my ‘GitHub’.

Which when compiled, using ‘g++ time.cpp -o time’ on the Pi, and run gives us a result of:

gareth@matilda:~/code/C++ $ g++ time.cpp -o time
gareth@matilda:~/code/C++ $ ./time
Little endian on: Linux.
Current time is: Mon Sep  7 11:10:48 2026
timestamp decimal and size: 1788775848 : 8
timestamp hex value: 6a9e8da8
timestamp in memory: a88d9e6a00000000
bswap32 parameter dec value: 1788775848
bswap32 parameter hex value: 6a9e8da8
bswap32 parameter in memory: a88d9e6a
byte 0 shifted: 0000006a
byte 1 shifted: 006a9e8d
byte 2 shifted: 9e8da800
byte 3 shifted: a8000000
byte 0 bitwise anded: 0000006a
byte 1 bitwise anded: 00009e00
byte 2 bitwise anded: 008d0000
byte 3 bitwise anded: a8000000
bswap32 all bytes swapped result dec value: 2827853418
bswap32 all bytes swapped result hex value: a88d9e6a
bswap32 all bytes swapped result in memory: 6a9e8da8
bswap64 parameter dec value: 1788775848
bswap64 parameter hex value: 000000006a9e8da8
bswap64 parameter in memory: a88d9e6a00000000
byte 0 shifted: 0000000000000000
byte 1 shifted: 0000000000000000
byte 2 shifted: 000000000000006a
byte 3 shifted: 00000000006a9e8d
byte 4 shifted: 0000006a9e8da800
byte 5 shifted: 006a9e8da8000000
byte 6 shifted: 9e8da80000000000
byte 7 shifted: a800000000000000
byte 0 bitwise anded: 0000000000000000
byte 1 bitwise anded: 0000000000000000
byte 2 bitwise anded: 0000000000000000
byte 3 bitwise anded: 0000000000000000
byte 4 bitwise anded: 0000006a00000000
byte 5 bitwise anded: 00009e0000000000
byte 6 bitwise anded: 008d000000000000
byte 7 bitwise anded: a800000000000000
bswap64 all bytes swapped result dec value: 12145537948191817728
bswap64 all bytes swapped result hex value: a88d9e6a00000000
bswap64 all bytes swapped result in memory: 000000006a9e8da8
Sending:
32: 6a9e8da8
64: 000000006a9e8da8
Received:
32: 6a9e8da8
64: 000000006a9e8da8
bswap32 parameter dec value: 2827853418
bswap32 parameter hex value: a88d9e6a
bswap32 parameter in memory: 6a9e8da8
byte 0 shifted: 000000a8
byte 1 shifted: 00a88d9e
byte 2 shifted: 8d9e6a00
byte 3 shifted: 6a000000
byte 0 bitwise anded: 000000a8
byte 1 bitwise anded: 00008d00
byte 2 bitwise anded: 009e0000
byte 3 bitwise anded: 6a000000
bswap32 all bytes swapped result dec value: 1788775848
bswap32 all bytes swapped result hex value: 6a9e8da8
bswap32 all bytes swapped result in memory: a88d9e6a
bswap64 parameter dec value: 12145537948191817728
bswap64 parameter hex value: a88d9e6a00000000
bswap64 parameter in memory: 000000006a9e8da8
byte 0 shifted: 00000000000000a8
byte 1 shifted: 0000000000a88d9e
byte 2 shifted: 000000a88d9e6a00
byte 3 shifted: 00a88d9e6a000000
byte 4 shifted: 8d9e6a0000000000
byte 5 shifted: 6a00000000000000
byte 6 shifted: 0000000000000000
byte 7 shifted: 0000000000000000
byte 0 bitwise anded: 00000000000000a8
byte 1 bitwise anded: 0000000000008d00
byte 2 bitwise anded: 00000000009e0000
byte 3 bitwise anded: 000000006a000000
byte 4 bitwise anded: 0000000000000000
byte 5 bitwise anded: 0000000000000000
byte 6 bitwise anded: 0000000000000000
byte 7 bitwise anded: 0000000000000000
bswap64 all bytes swapped result dec value: 1788775848
bswap64 all bytes swapped result hex value: 000000006a9e8da8
bswap64 all bytes swapped result in memory: a88d9e6a00000000
Received 32 time value: 1788775848 : 6a9e8da8
Received 64 time value: 1788775848 : 6a9e8da8
Received 32 time is: Mon Sep  7 11:10:48 2026
Received 64 time is: Mon Sep  7 11:10:48 2026
gareth@matilda:~/code/C++ $

Where we can see the time being fetched and conceptually sent from one device to another. What can be confusing is the distinction between the bytes in memory and what is presented. The output hexadecimal value is in big endian (as readers of English understand it) whereas in memory it is stored as little endian. I’ve tried to make this clear in the output.

On a big endian device, no swapping will take place, so this does need to be run on a little endian device for the full effect. What the functions ‘bswap32’ and ‘bswap64’ show is how the bytes are reordered to match the desired endianness, switching from one to the other.

Conclusion

I hope this sheds some light on how computers represent real and manipulate real world concepts purely as numbers.

What has struck me about writing this article, is that I had intended it to be shorter but kept finding that one concept lead to another that then needed to be explained. That is perhaps the fear of computing, that there is so much to know even for something that on the surface appears simple. That I could have gone further and combined different concepts together into a collection that is then sent and received as a byte stream. But that would have been too far.

What do you think please?

Gareth Barnard
Latest posts by Gareth Barnard (see all)
blank

Gareth Barnard

Gareth is a developer of numerous Moodle Themes including Essential (the most popular Moodle Theme ever), Adaptable, Foundation, and other plugins such as course formats, including Collapsed Topics and the Grid format.

Add a reply or comment...