Hoy les vengo con una duda a la que no se como responderme. Hasta el momento no había prestado atención al tema del endianess pero cuando uno va a almacenar valores en un archivo y los cuáles puede ser compartidos o leerse en diferentes equipos pueden empezar los peros si justamente sus procesadores son de distinto endian.
No me había percatado de esto sino fuera porque durante una de mis investigaciones para mi proyecto personal comentaba de esto. Y se me prendió la alarma.
¿Que es eso del endian? Pues básicamente el formato en como se representan los números de más de un byte en el procesador. Basicamente hay dos tipos: big-endian y litte-endian.
En el primero los bits más representativos están al principio, mientras que para little-endian en cambio son los menos representativos. Es decir están invertidos.
Ya estuve leyendo en la documentación de Lazarus que efectivamente hay maneras seguras de poder ir de un tipo de endian a otro. Y hay un buen repertorio de funciones para trabajar con los tipos enteros:
function SwapEndian(const AValue: SmallInt): SmallInt;{$ifdef SYSTEMINLINE}inline;{$endif} function SwapEndian(const AValue: Word): Word;{$ifdef SYSTEMINLINE}inline;{$endif} function SwapEndian(const AValue: LongInt): LongInt; function SwapEndian(const AValue: DWord): DWord; function SwapEndian(const AValue: Int64): Int64; function SwapEndian(const AValue: QWord): QWord; function BEtoN(const AValue: SmallInt): SmallInt;{$ifdef SYSTEMINLINE}inline;{$endif} function BEtoN(const AValue: Word): Word;{$ifdef SYSTEMINLINE}inline;{$endif} function BEtoN(const AValue: LongInt): LongInt;{$ifdef SYSTEMINLINE}inline;{$endif} function BEtoN(const AValue: DWord): DWord;{$ifdef SYSTEMINLINE}inline;{$endif} function BEtoN(const AValue: Int64): Int64;{$ifdef SYSTEMINLINE}inline;{$endif} function BEtoN(const AValue: QWord): QWord;{$ifdef SYSTEMINLINE}inline;{$endif} function LEtoN(const AValue: SmallInt): SmallInt;{$ifdef SYSTEMINLINE}inline;{$endif} function LEtoN(const AValue: Word): Word;{$ifdef SYSTEMINLINE}inline;{$endif} function LEtoN(const AValue: LongInt): LongInt;{$ifdef SYSTEMINLINE}inline;{$endif} function LEtoN(const AValue: DWord): DWord;{$ifdef SYSTEMINLINE}inline;{$endif} function LEtoN(const AValue: Int64): Int64;{$ifdef SYSTEMINLINE}inline;{$endif} function LEtoN(const AValue: QWord): QWord;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoBE(const AValue: SmallInt): SmallInt;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoBE(const AValue: Word): Word;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoBE(const AValue: LongInt): LongInt;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoBE(const AValue: DWord): DWord;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoBE(const AValue: Int64): Int64;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoBE(const AValue: QWord): QWord;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoLE(const AValue: SmallInt): SmallInt;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoLE(const AValue: Word): Word;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoLE(const AValue: LongInt): LongInt;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoLE(const AValue: DWord): DWord;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoLE(const AValue: Int64): Int64;{$ifdef SYSTEMINLINE}inline;{$endif} function NtoLE(const AValue: QWord): QWord;{$ifdef SYSTEMINLINE}inline;{$endif}
Gracias las funciones XtoN es posible leer ya sea BE (Big-endian) o LE (Litte-endian) y pasarlo al tipo nativo del procesador. Y a su inverso NtoX hacer lo opuesto.
De esta forma uno por ejemplo puede definir que sus archivos siempre se guarden en big-endian por ejemplo y proceder a emplear BEtoN() para leerlo y recuperar el dato sin problemas. Y usar NtoBE() al momento de guardarlo.
Hasta ahí todo bien... con los enteros. Pero luego recordé el hilo en el que escafandra daba una advertencia sobre como leer el signo de un doble y "adivinar" en que "mitad" saber leer.
Y ahora me asalta la duda ¿En los tipos flotantes también rige el sistema endianess? Porque me llama poderosamente la atención que si efectivamente es asi, ¿porqué es que no existe entonces las funciones sobrecargadas para los flotantes? O es que yo soy medio lento y no las encuentro.
Esto me lleva entonces a considerar a escribir mi propia implementación análogas a las detalladas anteriormente. Pensé en estudiar por el comienzo... a ver que es lo que hace en IsNan() y en IsInfinite():
function IsNan(const d : Double): Boolean; var fraczero, expMaximal: boolean; begin {$if defined(FPC_BIG_ENDIAN) or defined(FPC_DOUBLE_HILO_SWAPPED)} expMaximal := ((TSplitDouble(d).cards[0] shr 20) and $7ff) = 2047; fraczero:= (TSplitDouble(d).cards[0] and $fffff = 0) and (TSplitDouble(d).cards[1] = 0); {$else FPC_BIG_ENDIAN} expMaximal := ((TSplitDouble(d).cards[1] shr 20) and $7ff) = 2047; fraczero := (TSplitDouble(d).cards[1] and $fffff = 0) and (TSplitDouble(d).cards[0] = 0); {$endif FPC_BIG_ENDIAN} Result:=expMaximal and not(fraczero); end;
function IsInfinite(const d : Double): Boolean; var fraczero, expMaximal: boolean; begin {$if defined(FPC_BIG_ENDIAN) or defined(FPC_DOUBLE_HILO_SWAPPED)} expMaximal := ((TSplitDouble(d).cards[0] shr 20) and $7ff) = 2047; fraczero:= (TSplitDouble(d).cards[0] and $fffff = 0) and (TSplitDouble(d).cards[1] = 0); {$else FPC_BIG_ENDIAN} expMaximal := ((TSplitDouble(d).cards[1] shr 20) and $7ff) = 2047; fraczero := (TSplitDouble(d).cards[1] and $fffff = 0) and (TSplitDouble(d).cards[0] = 0); {$endif FPC_BIG_ENDIAN} Result:=expMaximal and fraczero; end;
Siendo TSplitDouble un simple record:
type TSplitDouble = packed record cards: Array[0..1] of cardinal; end;
Y Cardinal un simple alias del LongWord.
Pero hago aguas, al intentar razonarlo. ¿Alguna guía?
Saludos,