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(×tamp);
cout << ctime(×tamp);
int ts = sizeof(timestamp);
cout << "timestamp decimal and size: " << timestamp << " : " << ts << endl;
vector<uint8_t> timestampbytes(reinterpret_cast<uint8_t*>(×tamp), reinterpret_cast<uint8_t*>(×tamp) + 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?
- Endianness - 16th September 2026
- Crossover - 16th August 2026
- Think aiout it - 16th July 2026

