Bonjour,
Je travaille actuellement sur une librairie destinée à compacter / décompacter des entiers signés, codés sur 32 bits. Le but est de les coder sur 24, 16 ou 8 bits selon le cas pour les transmettre sur un canal de communication puis d’être capable d’effectuer la bijection inverse chez le récepteur.
J’ai utilisé deux techniques différentes pour réaliser ces fonctions. Je manque toutefois de recul (et d’expérience) pour porter un regard critique sur mon travail. C’est pourquoi je sollicite humblement votre avis :p
Première approche (la plus intuitive pour moi) :
J’ai utilisé une union pour mapper sur l’entier un tableau d’octet
Par cette technique, je suis capable d’accéder et de travailler sur les différents octets qui composent l’entier. Je test l’octet qui contient le bit de signe (qui correspond au MSB). Cette méthode n’est pas portable car je dois émettre une hypothèse sur sa position dans le tableau de char.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 union BufferInt32_u { int32_t entier; char_t octets[C_NB_OCTETS_32BITS]; }; typedef union BufferInt32_u bufferint32_t;
Voici, en exemple, la fonction qui réalise la conversion entier signé 32 -> 24 bits
Et, voici la fonction qui réalise l’opération inverse :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 int32_t RecadrerInt32Int24(int32_t Entier32bits, char_t p_EntierCompresse[C_NB_OCTETS_24BITS]) { bufferint32_t BufferInt32; size_t i; int32_t CompteRendu; char_t BufferResultat[C_NB_OCTETS_24BITS]; //Controler que l'entier rentre bien dans la taille mémoire finale spécifiée if ((Entier32bits >= C_MIN_INT_24BITS) && (Entier32bits <= C_MAX_INT_24BITS)) { //Recopie de l'entier a compresser dans une variable locale pour modification BufferInt32.entier = Entier32bits; //Replacer le bit de poids fort qui contient le signe BufferResultat[C_POS_OCTET_POIDS_FORT_24BITS] = (BufferInt32.octets[C_POS_OCTET_POIDS_FORT_32BITS]) & C_MASQUE_POIDS_FORT_OCTET; //Reconstruire l'octet de poids fort BufferResultat[C_POS_OCTET_POIDS_FORT_24BITS] |= BufferInt32.octets[C_POS_OCTET_POIDS_FORT_24BITS]; //Replacer les autres bits for (i = C_POSITION_OCTET_POIDS_FAIBLE; i < C_POS_OCTET_POIDS_FORT_24BITS; ++i) { BufferResultat[i] = BufferInt32.octets[i]; } CompteRendu = EXIT_SUCCESS; } else { //Positionner les bits à une valeur par defaut for (i = C_ZERO; i < C_NB_OCTETS_24BITS; ++i) { BufferResultat[i] = C_INT8_DEFAUT; } CompteRendu = EXIT_FAILURE; } //Assigner à la sortie la valeur du buffer for (i = C_ZERO; i < C_NB_OCTETS_24BITS; ++i) { p_EntierCompresse[i] = BufferResultat[i]; } return CompteRendu; }
La 2e technique est censée rendre la fonction portable en privilégiant les opérations de décalage de bits. Du coup, l’utilisation de l’union n’est plus justifiée.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 int32_t ReformerInt24Int32(char_t EntierCompresse24Bits[C_NB_OCTETS_24BITS], int32_t * p_Entier32bits) { bufferint32_t BufferInt32; int32_t CompteRendu; size_t i; //Initialisation du buffer BufferInt32.entier = C_ZERO; //Recopie de l'entier a compresser dans une variable locale pour modification for (i = C_ZERO; i < C_NB_OCTETS_24BITS; ++i) { BufferInt32.octets[i] = EntierCompresse24Bits[i]; } //Test du signe de l'entier a reformer if (EntierCompresse24Bits[C_POS_OCTET_POIDS_FORT_24BITS] >= C_ZERO) { //On complete l'entier avec des zeros BufferInt32.entier &= C_MASQUE_POSITIF_24_VERS_32BITS; } else { //On complete avec des uns BufferInt32.entier |= C_MASQUE_NEGATIF_24_VERS_32BITS; } CompteRendu = EXIT_SUCCESS; //Assigner à la sortie la valeur du buffer *p_Entier32bits = BufferInt32.entier; return CompteRendu; }
Ce code fonctionne également mais la norme C99 (chapitre 6.5.7) précise que le décalage de bits sur des entiers signés est dangeureux. J’ai modifié la fonction pour travailler sur des entiers non signés :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 int32_t Test(int32_t Entier32bits, char_t p_EntierCompresse[C_NB_OCTETS_24BITS]) { size_t CompteurOctets; for (CompteurOctets = C_ZERO; CompteurOctets < C_NB_OCTETS_24BITS; ++CompteurOctets) { p_EntierCompresse[CompteurOctets] = (char_t) ((Entier32bits >> (C_NB_BITS_OCTET * CompteurOctets)) & 0x000000FF); } if(Entier32bits >> (C_NB_BITS_OCTET * C_NB_OCTETS_32BITS - C_UN)) { //Le nombre est negatif p_EntierCompresse[C_NB_OCTETS_24BITS - C_UN]|= 0x80; } else { //Le nombre est positif p_EntierCompresse[C_NB_OCTETS_24BITS - C_UN]&= 0x7F; } return EXIT_SUCCESS; }
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 int32_t Test(int32_t Entier32bits, char_t p_EntierCompresse[C_NB_OCTETS_24BITS]) { size_t CompteurOctets; uint32_t EntierNonSigne = (uint32_t) Entier32bits; for (CompteurOctets = C_ZERO; CompteurOctets < C_NB_OCTETS_24BITS; ++CompteurOctets) { p_EntierCompresse[CompteurOctets] = (char_t) ((EntierNonSigne >> (C_NB_BITS_OCTET * CompteurOctets)) & 0x000000FF); } if(EntierNonSigne >> (C_NB_BITS_OCTET * C_NB_OCTETS_32BITS - C_UN)) { //Le nombre est negatif p_EntierCompresse[C_NB_OCTETS_24BITS - C_UN]|= 0x80; } else { //Le nombre est positif p_EntierCompresse[C_NB_OCTETS_24BITS - C_UN]&= 0x7F; } return EXIT_SUCCESS; }
Mes questions :
- dans la mesure où je n’utilise pas la valeur de l’entier « décalé », est-il vraiment nécessaire de passer par l’entier non signé pour obtenir un code portable ?
- autre technique :
- créer deux jeux de fonctions, chacun supposant le bit de signe en début ou fin du tableau d’octets
- créer deux jeux de fonctions, chacun supposant le bit de signe en début ou fin du tableau d’octets
- tester la position du bits de signe dans le tableau d’octets et affecter à la bonne fonction l’alias RecadrerInt32IntXX
Qu’en pensez-vous ?
Merci d'avance,
Les headers :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 #define C_DECALAGE_UN_BIT 1 #define C_NB_BITS_OCTET 8 #define C_NB_OCTETS_32BITS 4 #define C_NB_OCTETS_24BITS 3 #define C_NB_OCTETS_16BITS 2 #define C_NB_OCTETS_8BITS 1 //Constantes des tableaux de bits #define C_POS_BIT_POIDS_FORT_OCTET (C_NB_BITS_OCTET - 1 + C_PREMIER_INDICE) #define C_POS_BIT_POIDS_FAIBLE_OCTET C_PREMIER_INDICE //Constantes des tableaux d'octets #define C_POSITION_OCTET_POIDS_FAIBLE C_PREMIER_INDICE #define C_POS_OCTET_POIDS_FORT_32BITS (C_NB_OCTETS_32BITS - 1 + C_PREMIER_INDICE) #define C_POS_OCTET_POIDS_FORT_24BITS (C_NB_OCTETS_24BITS - 1 + C_PREMIER_INDICE) //Constantes des masques de conversion #define C_MASQUE_POIDS_FAIBLE_OCTET 0x01 #define C_MASQUE_POIDS_FORT_OCTET 0x80 #define C_MASQUE_POSITIF_24_VERS_32BITS 0x00FFFFFF #define C_MASQUE_NEGATIF_24_VERS_32BITS 0xFF000000 #define C_MIN_INT_24BITS -8388608 #define C_MAX_INT_24BITS -C_MIN_INT_24BITS-1 //Valeurs par defaut #define C_INT8_DEFAUT 0 #define C_INT32_DEFAUT 0
Partager