Dependiendo del sistema operativo en que se trabaje, hay que
considerar el orden de los bytes en los tipos de datos numéricos que
utilizan varios bytes. Existen dos formatos diferentes, denominados "Little
Endian" y "Big Endian".
"Little Endian" significa que el byte de menor peso
se almacena en la dirección más baja de memoria y el byte de mayor
peso en la más alta.
Así, un Long Int de 4 bytes
Byte3 Byte2 Byte1 Byte0
se almacenará en memoria de la siguiente manera:
Dirección Base +0 ===> Byte0
Dirección Base +1 ===> Byte1
Dirección Base +2 ===> Byte2
Dirección Base +3 ===> Byte3
Los procesadores Intel (usados en la mayoría de los ordenadores
personales) y los DEC Alpha RISC son "Little Endian".
En el formato "Big Endian" el byte de mayor peso se
almacena en la dirección más baja de memoria y el byte de menor
peso en la dirección más alta.
El Long Int anterior, se almacenaría ahora así:
Dirección Base +0 ===> Byte3
Dirección Base +1 ===> Byte2
Dirección Base +2 ===> Byte1
Dirección Base +3 ===> Byte0
La mayoría de los sistemas UNIX, el protocolo de Internet
TCP, los procesadores Motorola 680x0 (y, por lo tanto, los Macintosh), Hewlett-Packard
PA-RISC, y Sun SuperSPARC son "Big Endian". El MIPS de Silicon Graphics
y el procesador IBM/Motorola PowerPC son capaces de entender ambos sistemas,
por lo que se dice que son "bi-endian".
¿Qué ordenación es mejor?
Debido a la histórica rivalidad entre el PC y el Mac,
la cuestión de la ordenación de los bytes ha sido ampliamente
discutido. Ambos sistemas tienen ventajas e inconvenientes, que presentamos
a continuación.
En la forma "Little Endian", las instrucciones en ensamblador
para elegir 1, 2, 4 o un número mayor de bytes proceden de la misma forma:
primero se lee el byte de menor peso, que está en el offset (desplazamiento)
0.
Además, la relación 1:1 entre el offset y el número
del byte hace que las rutinas matemáticas sean más sencillas de
implementar.
En la forma "Big Endian", al tener el byte de mayor
peso el primero, se puede comprobar de forma directa si el número es
positivo o negativo sólo comprobando el primer byte (recordemos que el
signo se almacena en el primer bit) y sin necesidad de saber la longitud del
número.
Esta forma de representación coincide con el orden en
que los números son escritos, de modo que las rutinas de conversión
entre sistemas de numeración son más eficientes que si se realizaran
en "Little Endian".
Importancia de conocer los diferentes formatos
A la hora de realizar un programa, puede ser necesario conseguir
que éste sepa diferenciar entre ambos formatos y actúe consecuentemente
al que se esté utilizando.
Si se desea que un programa funcione en varias plataformas hay
que tener en cuenta si éstas usan el mismo formato, y si es distinto
adaptar el programa a ello. De lo contrario, si se utilizaran ficheros binarios
el programa no funcionaría, al tomar valores incorrectos.
El protocolo TCP usa el formato "Big Endian", por lo
que los sistemas que usan "Little Endian" deben convertir los datos
al crear los paquetes TCP/IP.
Los formatos "Little Endian" y "Big Endian"
pueden aplicarse, además de a la ordenación de los bytes, a la
ordenación de los bits.
En un byte, un sistema que utilice "Little Endian"
tendrá el bit de menor peso en el primer bit y el bit de mayor peso en
el último bit. En un sistema "Big Endian", el bit de mayor
peso estará en el primer bit y el de menor peso en el último.
Al aplicarse tanto a la ordenación de bytes como de bits,
aprovechando las ventajas de cada uno, podemos encontrar sistemas en los que
se utiliza el formato "Big Endian" para los bytes y el "Little
Endian" para la ordenación de los bits internamente en cada byte,
o viceversa.
Cambiar de un formato a otro
Para cambiar entre un formato y otro, podemos construir una función
que intercambie los bytes. El procedimiento es sencillo:
Formato Original --> A B C D
Formato Cambiado --> D C B A
Además, dada la simetría entre ambos, la misma
función nos sirve tanto para pasar de "Little Endian" a "Big
Endian" como de "Big Endian" a "Little Endian".
void Intercambio(long int *a)
{
long int b0,b1,b2,b3;
b0=(*a)&0x000000FF;
b1=((*a)&0x0000FF00)>>8;
b2=((*a)&0x00FF0000)>>16;
b3=((*a)&0xFF000000)>>24;
*a=(b0<<24) | (b1<<16) | (b2<<8) | b3;
}
/* versión más eficiente */
void Intercambio2(long int *a)
{
*a=(((*a)&0x000000FF)<<24) | ((((*a)&0x0000FF00)>>8)<<16) | \ ((((*a)&0x00FF0000)>>16)<<8) | (((*a)&0xFF000000)>>24);
}
|