web-dev-qa-db-fra.com

Conversion Little Endian en Big Endian

Je veux juste demander si ma méthode est correcte pour convertir de little endian en big endian, juste pour m'assurer que je comprends la différence.

J'ai un numéro qui est stocké dans little-endian, voici les représentations binaires et hexagonales du nombre:

‭0001 0010 0011 0100 0101 0110 0111 1000‬

‭12345678‬

Au format big-endian, je pense que les octets doivent être permutés, comme ceci:

1000 0111 0110 0101 0100 0011 0010 0001

‭87654321

Est-ce correct?

En outre, le code ci-dessous tente de le faire mais échoue. Y a-t-il quelque chose qui cloche ou puis-je optimiser quelque chose? Si le code est mauvais pour cette conversion, pouvez-vous expliquer pourquoi et indiquer une meilleure méthode pour effectuer la même conversion?

uint32_t num = 0x12345678;
uint32_t b0,b1,b2,b3,b4,b5,b6,b7;
uint32_t res = 0;

b0 = (num & 0xf) << 28;
b1 = (num & 0xf0) << 24;
b2 = (num & 0xf00) << 20;
b3 = (num & 0xf000) << 16;
b4 = (num & 0xf0000) << 12;
b5 = (num & 0xf00000) << 8;
b6 = (num & 0xf000000) << 4;
b7 = (num & 0xf0000000) << 4;

res = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7;

printf("%d\n", res);
20
JeckyPorter

Le code exemple d'OP est incorrect.

La conversion Endian fonctionne au niveau des bits et des octets 8 bits. La plupart des problèmes endian concernent le niveau d'octet. Le code OP effectue un changement final au niveau du quartet 4 bits. Recommander à la place:

// Swap endian (big to little) or (little to big)
uint32_t num = 9;
uint32_t b0,b1,b2,b3;
uint32_t res;

b0 = (num & 0x000000ff) << 24u;
b1 = (num & 0x0000ff00) << 8u;
b2 = (num & 0x00ff0000) >> 8u;
b3 = (num & 0xff000000) >> 24u;

res = b0 | b1 | b2 | b3;

printf("%" PRIX32 "\n", res);

Si les performances sont vraiment importantes, le processeur en question devra être connu. Sinon, laissez le compilateur.

[Modifier] OP a ajouté un commentaire qui change les choses.
"La valeur numérique 32 bits représentée par la représentation hexadécimale (st uv wx yz) doit être enregistrée dans un champ de quatre octets sous la forme (st uv wx yz)."

Il semble que dans ce cas, l’endian du nombre 32 bits est unknown et le résultat doit être stocké en mémoire dans peu ordre endian.

uint32_t num = 9;
uint8_t b[4];
b[0] = (uint8_t) (num >>  0u);
b[1] = (uint8_t) (num >>  8u);
b[2] = (uint8_t) (num >> 16u);
b[3] = (uint8_t) (num >> 24u);

[2016 Éditer] Simplification

... Le type du résultat est celui de l'opérande gauche promu .... Opérateurs de décalage binaire C11 §6.5.7 3 

L'utilisation de u après le shift constantes (opérandes de droite) donne le même résultat que sans.

b3 = (num & 0xff000000) >> 24u;
b[3] = (uint8_t) (num >> 24u);
// same as 
b3 = (num & 0xff000000) >> 24;
b[3] = (uint8_t) (num >> 24);
29
chux

Je pense que vous pouvez utiliser la fonction htonl(). L'ordre des octets du réseau est big endian.

22
aoak

"J'échange chaque octet non?" -> oui, pour convertir entre le petit et le gros endian, il suffit de donner aux octets l’ordre inverse . Mais au début, réalisez quelques petites choses:

  • la taille de uint32_t est de 32 bits, soit 4 octets, soit 8 chiffres HEX.
  • mask 0xf récupère les 4 bits les moins significatifs, pour récupérer 8 bits, vous avez besoin de 0xff

ainsi, si vous voulez échanger l'ordre de 4 octets avec ce type de masque, vous pouvez:

uint32_t res = 0;
b0 = (num & 0xff) << 24;        ; least significant to most significant
b1 = (num & 0xff00) << 8;       ; 2nd least sig. to 2nd most sig.
b2 = (num & 0xff0000) >> 8;     ; 2nd most sig. to 2nd least sig.
b3 = (num & 0xff000000) >> 24;  ; most sig. to least sig.
res = b0 | b1 | b2 | b3 ;
8
LihO

Vous pourriez faire ceci:

int x = 0x12345678;

x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24)  ; 

printf("value = %x", x);  // x will be printed as 0x78563412
4
ayyappa ch

Je suppose que vous êtes sur Linux

Inclure "byteswap.h" & Utiliser int32_t bswap_32(int32_t argument); 

C'est une vue logique, voir, /usr/include/byteswap.h

1
0x47-sci-tech

Le code de l'OP est incorrect pour les raisons suivantes:

  • Les échanges sont effectués sur une limite de quartet (4 bits) au lieu d'une limite d'octet (8 bits).
  • Les opérations shift-left << des quatre derniers swaps sont incorrectes. Elles doivent être des opérations shift-right >> et leurs valeurs de décalage doivent également être corrigées.
  • L'utilisation du stockage intermédiaire n'est pas nécessaire et le code peut donc être réécrit pour être plus concis/reconnaissable. Ce faisant, certains compilateurs seront en mesure d’optimiser davantage le code en reconnaissant le motif souvent utilisé.

Considérez le code suivant, qui convertit efficacement une valeur non signée:

// Swap endian (big to little) or (little to big)
uint32_t num = 0x12345678;
uint32_t res =
    ((num & 0x000000FF) << 16) |
    ((num & 0x0000FF00) << 8) |
    ((num & 0x00FF0000) >> 8) |
    ((num & 0xFF000000) >> 16);

printf("%0x\n", res);

Le résultat est représenté ici en binaire et en hexadécimal, remarquez comment les octets ont été échangés:

‭0111 1000 0101 0110 0011 0100 0001 0010‬

78563412

L'optimisation

En termes de performances, laissez au compilateur le soin d’optimiser votre code lorsque cela est possible. Évitez les structures de données inutiles telles que les tableaux pour les algorithmes simples comme celui-ci. Cela entraînerait généralement un comportement d'instruction différent, tel que l'accès à RAM au lieu d'utiliser des registres de la CPU.

1
Shaun Wilson

Une manière légèrement différente de résoudre ce problème qui peut parfois être utile consiste à avoir une union de la valeur de seize ou trente-deux bits et un tableau de caractères. Je viens de faire cela lorsque je reçois des messages série entrants avec un ordre big endian, mais je travaille sur un petit micro endian. 

union MessageLengthUnion {

uint16_t asInt;
uint8_t asChars[2];

};

Puis, lorsque je reçois les messages, je mets le premier reçu uint8 dans .asChars [1], le second dans .asChars [0], puis je l’accède en tant que partie .asInt du syndicat dans le reste de mon programme. Si vous avez une valeur de trente-deux bits à stocker, vous pouvez avoir un tableau long de quatre. 

1
DiBosco

Un programme simple C pour convertir de petit à grand

#include <stdio.h>

int main() {
unsigned int little=0x1234ABCD,big=0;
unsigned char tmp=0,l;

printf(" Little endian little=%x\n",little);

for(l=0;l < 4;l++) 
{
    tmp=0;
    tmp = little | tmp;
    big = tmp | (big << 8);
    little = little >> 8;
}
printf(" Big endian big=%x\n",big);

return 0;
}
1
Naresh pothula

une autre suggestion:

unsigned int a = 0xABCDEF23;
a = ((a&(0x0000FFFF)) << 16) | ((a&(0xFFFF0000)) >> 16);
a = ((a&(0x00FF00FF)) << 8) | ((a&(0xFF00FF00)) >>8);
printf("%0x\n",a);
1
Saurabh Sengar

Vous pouvez utiliser les fonctions lib. Ils se résument à Assembly, mais si vous êtes ouvert à d'autres implémentations en C, les voici (en supposant que int est 32 bits):

void byte_swap16(unsigned short int *pVal16) {

//#define method_one 1
// #define method_two 1
#define method_three 1
#ifdef method_one
    unsigned char *pByte;

    pByte = (unsigned char *) pVal16;
    *pVal16 = (pByte[0] << 8) | pByte[1];
#endif

#ifdef method_two
    unsigned char *pByte0;
    unsigned char *pByte1;

    pByte0 = (unsigned char *) pVal16;
    pByte1 = pByte0 + 1;
    *pByte0 = *pByte0 ^ *pByte1;
    *pByte1 = *pByte0 ^ *pByte1;
    *pByte0 = *pByte0 ^ *pByte1;
#endif

#ifdef method_three
    unsigned char *pByte;

    pByte = (unsigned char *) pVal16;
    pByte[0] = pByte[0] ^ pByte[1];
    pByte[1] = pByte[0] ^ pByte[1];
    pByte[0] = pByte[0] ^ pByte[1];
#endif


}



void byte_swap32(unsigned int *pVal32) {

#ifdef method_one
    unsigned char *pByte;

    // 0x1234 5678 --> 0x7856 3412  
    pByte = (unsigned char *) pVal32;
    *pVal32 = ( pByte[0] << 24 ) | (pByte[1] << 16) | (pByte[2] << 8) | ( pByte[3] );
#endif

#if defined(method_two) || defined (method_three)
    unsigned char *pByte;

    pByte = (unsigned char *) pVal32;
    // move lsb to msb
    pByte[0] = pByte[0] ^ pByte[3];
    pByte[3] = pByte[0] ^ pByte[3];
    pByte[0] = pByte[0] ^ pByte[3];
    // move lsb to msb
    pByte[1] = pByte[1] ^ pByte[2];
    pByte[2] = pByte[1] ^ pByte[2];
    pByte[1] = pByte[1] ^ pByte[2];
#endif
}

Et l'utilisation est effectuée comme suit:

unsigned short int u16Val = 0x1234;
byte_swap16(&u16Val);
unsigned int u32Val = 0x12345678;
byte_swap32(&u32Val);
0
netskink