Intel processors (and maybe some others) use the little endian format for storage.
I always wonder why someone would want to store the bytes in reverse order. Does this format have any advantages over the big endian format?
Intel processors (and maybe some others) use the little endian format for storage.
I always wonder why someone would want to store the bytes in reverse order. Does this format have any advantages over the big endian format?
There are arguments either way, but one point is that in a little-endian system, the address of a given value in memory, taken as a 32, 16, or 8 bit width, is the same.
In other words, if you have in memory a two byte value:
0x00f0 16
0x00f1 0
taking that '16' as a 16-bit value (c 'short' on most 32-bit systems) or as an 8-bit value (generally c 'char') changes only the fetch instruction you use — not the address you fetch from.
On a big-endian system, with the above layed out as:
0x00f0 0
0x00f1 16
you would need to increment the pointer and then perform the narrower fetch operation on the new value.
So, in short, ‘on little endian systems, casts are a no-op.’
OK, here's the reason as I've had it explained to me: Addition and subtraction
When you add or subtract multi-byte numbers, you have to start with the least significant byte. If you're adding two 16-bit numbers for example, there may be a carry from the least significant byte to the most significant byte, so you have to start with the least significant byte to see if there is a carry. This is the same reason that you start with the rightmost digit when doing longhand addition. You can't start from the left.
Consider an 8-bit system that fetches bytes sequentially from memory. If it fetches the least significant byte first, it can start doing the addition while the most significant byte is being fetched from memory. This parallelism is why performance is better in little endian on such as system. If it had to wait until both bytes were fetched from memory, or fetch them in the reverse order, it would take longer.
This is on old 8-bit systems. On a modern CPU I doubt the byte order makes any difference and we use little endian only for historical reasons.
I always wonder why someone would want to store the bytes in reverse order.
Big-endian and little-endian are only "normal order" and "reverse order" from a human perspective, and then only if all of these are true...
Those are all human conventions that don't matter at all to a CPU. If you were to retain #1 and #2, and flip #3, little-endian would seem "perfectly natural" to people who read Arabic or Hebrew, which are written right-to-left.
And there are other human conventions that make big-endian that seem unnatural, like...
Back when I was mostly programming 68K and PowerPC, I considered big-endian to be "right" and little-endian to be "wrong". But since I've been doing more ARM and Intel work, I've gotten used to little-endian. It really doesn't matter.
sed
- which we had to fix for an "Advanced C in Unix" class - was badly broken on all platforms that weren't 32-bit little-endian)
Commented
Jul 25, 2011 at 16:08
With 8 bit processors it was certainly more efficienct, you could perform an 8 or 16bit operation without needing different code and without needing to buffer extra values.
It's still better for some addition operations if you are dealing a byte at a time.
But there is no reason that big-endian is more natural - in English you use thirteen (little endian) and twenty three (big endian)
0x12345678
is stored as 78 56 34 12
whereas on a BE system it’s 12 34 56 78
(byte 0 is on the left, byte 3 is on the right). Note how the larger the number is (in terms of bits), the more swapping it requires; a WORD would require one swap; a DWORD, two passes (three total swaps); a QWORD three passes (7 total), and so on. That is, (bits/8)-1
swaps. Another option is reading them both forwards and backwards (reading each byte forwards, but scanning the whole # backwards).
0x12345678
is stored as 87 65 43 21
, depending on how one chooses to write it. How one writes something rather arbitrary (read as: context dependent).
Commented
Jan 26, 2014 at 10:37
Nobody else has answered WHY this might be done, lots of stuff about consequences.
Consider an 8 bit processor which can load a single byte from memory in a given clock cycle.
Now, if you want to load a 16 bit value, into (say) the one and only 16 bit register you have - ie the program counter, then a simple way to do it is:
the outcome: you only ever increment the fetch location, you only ever load into the low order part of you wider register, and you only need to be able to shift left. (Of course, shifting right is helpful for other operations so this one is a bit of a side show.)
A consequence of this is that the 16 bit (double byte) stuff is stored in order Most..Least. I.e., the smaller address has the most significant byte - so big endian.
If you instead tried to load using little endian, you would need to load a byte into the lower part of your wide register, then load the next byte into a staging area, shift it, and then pop it into the top of your wider register. Or use a more complex arrangement of gating to be able to selectively load into the top or bottom byte.
The result of trying to go little endian is you either need more silicon (switches and gates), or more operations.
In other words, in terms of getting bang for buck back in the old days, you got more bang for most performance and smallest silicon area.
These days, these considerations and pretty much irrelevant, but things like pipeline fill may still be a bit of a big deal.
When it comes to writing s/w, life is frequently easier when using little endian addressing.
(And the big endian processors tend to be big endian in terms of byte ordering and little endian in terms of bits-in-bytes. But some processors are strange and will use big endian bit ordering as well as byte ordering. This makes life very interesting for the h/w designer adding memory-mapped peripherals but is of no other consequence to the programmer.)
The Japanese date convention is "big endian" - yyyy/mm/dd. This is handy for sorting algorithms, which can use a simple string-compare with the usual first-character-is-most-significant rule.
Something similar applies for big-endian numbers stored in a most-significant-field-first record. The significance order of the bytes within the fields matches the significance of the fields within the record, so you can use a memcmp
to compare records, not caring much whether you're comparing two longwords, four words, or eight separate bytes.
Flip the order of significance of the fields and you get the same advantage, but for little-endian numbers rather than big-endian.
This has very little practical significance, of course. Whether your platform is big-endian or little-endian, you can order a records fields to exploit this trick if you really need to. It's just a pain if you need to write portable code.
I may as well include a link to the classic appeal...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
EDIT
An extra thought. I once wrote a big integer library (to see if I could), and for that, the 32-bit-wide chunks are stored in little-endian order, irrespective of how the platform orders the bits in those chunks. The reasons were...
A lot of algorithms just naturally start working at the least significant end, and want those ends to be matched. For example in addition, the carries propogate to more and more significant digits, so it makes sense to start at the least significant end.
Growing or shrinking a value just means adding/removing chunks at the end - no need to shift chunks up/down. Copying may still be needed due to memory reallocation, but not often.
This has no obvious relevance to processors, of course - until CPUs are made with hardware big-integer support, it's purely a library thing.
jimwise made a good point. There is another issue, in little endian you can do the following:
byte data[4];
int num=0;
for(i=0;i<4;i++)
num += data[i]<<i*8;
OR
num = *(int*)&data; //is interpreted as
mov dword data, num ;or something similar it has been some time
More straight forward for programmers which are not affected by the obvious disadvantage of swapped locations in the memory. I personally find big endian to be inverse of what is natural :). 12 should be stored and written as 21 :)
for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }
corresponds to move.l data, num
on a big endian CPU.
Commented
Jul 25, 2011 at 22:34
The main reason is that a cast operation itself becomes invisible to hardware; recasting a pointer does not require an arithmetic operation to change the address of a pointer and does not rely on the compiler knowing the previous type to know how much to offset the pointer by. To overcome this, the pointer could always point to the end of the data in memory but this is more unintuitive than using little endian to solve the the problem
Let's say someone stored a dword 00 00 00 01 in big endian format at 0x100. You'd need to know the format of the data at the address to be able to read the value you want. With little endian you'd just be able to read a byte from 0x100 and it would be 1, with big endian, you need to know that a dword was stored there and read from 0x103. This abstracts the details of the memory layout less than little endian does because the programmer needs to be aware of the representation of the data.
At the hardware level, it may simplify store to load forwarding, because if you write a dword to 0x100 and then read a byte, it would be reading the same address on little endian rather than 0x103. This means store to load forwarding in the store buffer for a byte would require 0 checks. When you load a byte at 0x103, you'd have to check other addresses in the store buffer and their data lengths.
The answers talking about being able to immediately perform an arithmetic operation on the lowest byte while fetching the highest byte is wrong because on big endian, it could just fetch the highest byte first.
I always wonder why someone would want to store the bytes in reverse order
Decimal number are written big endian. It also how you write it in English You start with the most significant digit and the next most significant to the least most significant. e.g.
1234
is one thousand, two hundred and thirty four.
This is way big endian is sometimes called the natural order.
In little endian, this number would be one, twenty, three hundred and four thousand.
However, when you perform arithmetic like addition or subtraction, you start with the end.
1234
+ 0567
====
You start with 4 and 7, write the lowest digit and remember the carry. Then you add 3 and 6 etc. For add, subtract or comparison, it is simpler to implement, if you already have logic to read the memory in order, if the numbers are reversed.
To support big endian this way, you need logic to read memory in reverse, or you have RISC process which only operates on registers. ;)
A lot of the Intel x86/Amd x64 design is historical.
Big-endian is useful for some operations (comparisons of "bignums" of equal octet-length springs to mind). Little-endian for others (adding two "bignums", possibly). In the end, it depends on what the CPU hardware has been set up for, it's usually one or the other (some MIPS chips were, IIRC, switchable on boot to be LE or BE).
When only storage and transfer with variable lengths are involved, but no arithmetics with multiple values, then LE is usually easier to write, while BE is easier to read.
Let's take an int-to-string conversion (and back) as a specific example.
int val_int = 841;
char val_str[] = "841";
When the int is converted to the string, then the least significant digit is easier to extract than the most significant digit. It can all be done in a simple loop with a simple end condition.
val_int = 841;
// Make sure that val_str is large enough.
i = 0;
do // Write at least one digit to care for val_int == 0
{
// Constants, can be optimized by compiler.
val_str[i] = '0' + val_int % 10;
val_int /= 10;
i++;
}
while (val_int != 0);
val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it
Now try the same in BE order. Usually you need another divisor that holds the largest power of 10 for the specific number (here 100). You first need to find this, of course. Much more stuff to do.
The string to int conversion is easier to do in BE, when it is done as the reverse write operation. Write stores the most significant digit last, so it should be read first.
val_int = 0;
length = strlen(val_str);
for (i = 0; i < length; i++)
{
// Again a simple constant that can be optimized.
val_int = 10*val_int + (val_str[i] - '0');
}
Now do the same in LE order. Again, you'd need an additional factor starting with 1 and being multiplied by 10 for each digit.
Thus I usually prefer to use BE for storage, because a value is written exactly once, but read at least once and maybe many times. For its simpler structure, I usually also go the route to convert to LE and then reverse the result, even if it writes the value a second time.
Another example for BE storage would be UTF-8 encoding, and many more.