OVH Cloud OVH Cloud

Endianness, hton* ...

15 réponses
Avatar
Hamiral
Bonjour,

Je veux lire un fichier dans un format binaire bien documenté, or la
documentation de ce format me dit que toutes les valeurs numériques sont
stockées en "network byte order", c'est-à-dire big endian si j'ai bien
compris.
Pour lire ces données numériques, j'ai donc pensé aux fonctions de la
série htonl, nltoh etc. dont j'ai entendu parler ici. J'ai donc lu les
manpages de ces fonctions, et deux problèmes se posent à moi :
1/ ce sont des fonctions de la norme BSD.
2/ elles ne permettent de traduire que des types entiers et non des
flottants.

Je me demandais donc si vous connaissiez une autre alternative pour
effectuer la traduction, ou bien si je vais être obligé d'écrire mes
propres routines de conversions.

Merci d'avance,
--
Hamiral

10 réponses

1 2
Avatar
Pierre Maurette
Hamiral a écrit:

Bonjour,

Je veux lire un fichier dans un format binaire bien documenté, or la
documentation de ce format me dit que toutes les valeurs numériques sont
stockées en "network byte order", c'est-à-dire big endian si j'ai bien
compris.
Pour lire ces données numériques, j'ai donc pensé aux fonctions de la
série htonl, nltoh etc. dont j'ai entendu parler ici. J'ai donc lu les
manpages de ces fonctions, et deux problèmes se posent à moi :
1/ ce sont des fonctions de la norme BSD.
2/ elles ne permettent de traduire que des types entiers et non des
flottants.

Je me demandais donc si vous connaissiez une autre alternative pour
effectuer la traduction, ou bien si je vais être obligé d'écrire mes
propres routines de conversions.
Hors bibliothèque spécifiquement orientée TCP/IP, vous êtes de toutes

façons obligé de faire des tests/hypothèses. Le format des flottants
doit être IEEE754. En faisant l'hypothèse que les implantations en
mémoire d'un entier et d'un flottant de même taille suivent les même
règles, vous pourriez essayer d'utiliser htonl, nltoh etc. si par
exemple sizeof(int) == sizeof(float). Mais ça semble bien foireux.

Si vous acceptez qu'il n'existe que des machines grand-boutiennes qui
vont lire directement vos binaires, et des machines petit-boutiennes
sur la base du char, à la fois pour les flottants et les entiers, il
n'y a qu'un test et une fonction à écrire:

unsigned char * AjusteBig2Little(unsigned char * zone, size_t n)
{
unsigned char tempo;
int i = 0;
int j = n-1;
assert( zone != NULL && n > 0);
while (i < j)
{
tempo = zone[i];
zone[i] = zone[j];
zone[j] = tempo;
i++, j--;
}
return zone;
}

#define AJUSTEBIG2LITTLE(var) AjusteBig2Little((unsigned char*)&var,
sizeof(var))

(on peut ne pas aimer le retour unsigned char * et le #define).
--
Pierre

Avatar
AG
Hamiral wrote:
Bonjour,

Je veux lire un fichier dans un format binaire bien documenté, or la
documentation de ce format me dit que toutes les valeurs numériques sont
stockées en "network byte order", c'est-à-dire big endian si j'ai bien
compris.
Pour lire ces données numériques, j'ai donc pensé aux fonctions de la
série htonl, nltoh etc. dont j'ai entendu parler ici. J'ai donc lu les
manpages de ces fonctions, et deux problèmes se posent à moi :
1/ ce sont des fonctions de la norme BSD.
De toute façon, si vous faites une application réseaux, vous serez

obligé d'utiliser des fonctions spécifiques à votre systeme d'exploitation.

AG

Avatar
Hamiral
De toute façon, si vous faites une application réseaux, vous serez
obligé d'utiliser des fonctions spécifiques à votre systeme d'exploitation.

AG


Nonon, cela n'a rien à voir avec les réseaux, c'est juste que le format
de fichier est décrit comme cela (pour info il s'agit du format
lightwave LWO2).

--
Hamiral

Avatar
Hamiral
Hors bibliothèque spécifiquement orientée TCP/IP, vous êtes de toutes
façons obligé de faire des tests/hypothèses.


Ce format fichier n'a rien à voir avec les réseaux, mais sa
documentation dit que les données numériques sont stockées en "network
byte order".

Le format des flottants
doit être IEEE754.


C'est ce que dit la documentation du format de fichier

En faisant l'hypothèse que les implantations en
mémoire d'un entier et d'un flottant de même taille suivent les même
règles, vous pourriez essayer d'utiliser htonl, nltoh etc. si par
exemple sizeof(int) == sizeof(float). Mais ça semble bien foireux.


J'y avais pensé, mais cela ne me plaisait pas ...

Si vous acceptez qu'il n'existe que des machines grand-boutiennes qui
vont lire directement vos binaires, et des machines petit-boutiennes
sur la base du char, à la fois pour les flottants et les entiers, il
n'y a qu'un test et une fonction à écrire:

unsigned char * AjusteBig2Little(unsigned char * zone, size_t n)
{
unsigned char tempo;
int i = 0;
int j = n-1;
assert( zone != NULL && n > 0);
while (i < j)
{
tempo = zone[i];
zone[i] = zone[j];
zone[j] = tempo;
i++, j--;
}
return zone;
}


Cela va-t-il réellement marcher ?
Il m'avait semblé que pour des dword les octets étaient échangés word
par word.
je ne suis pas très clair voici un exemple tel que j'avais compris la
chose :
0xAABBCCDD en big endian donnerait 0xCCDDAABB

Ou je me trompe et il donne simplement 0xDDCCBBAA ?? (Dans ce cas
évidemment mon problème était trivial et je vous ai embêtés pour rien :)


#define AJUSTEBIG2LITTLE(var) AjusteBig2Little((unsigned char*)&var,
sizeof(var))

(on peut ne pas aimer le retour unsigned char * et le #define).


Cela ne me dérange pas :)


--
Hamiral

Avatar
Pierre Maurette
Hamiral a écrit:

Hors bibliothèque spécifiquement orientée TCP/IP, vous êtes de toutes
façons obligé de faire des tests/hypothèses.


Ce format fichier n'a rien à voir avec les réseaux, mais sa
documentation dit que les données numériques sont stockées en "network
byte order".
OK. Le problème reste de le même.


Si vous acceptez qu'il n'existe que des machines grand-boutiennes qui
vont lire directement vos binaires, et des machines petit-boutiennes
sur la base du char, à la fois pour les flottants et les entiers, il
n'y a qu'un test et une fonction à écrire:

unsigned char * AjusteBig2Little(unsigned char * zone, size_t n)
{
unsigned char tempo;
int i = 0;
int j = n-1;
assert( zone != NULL && n > 0);
while (i < j)
{
tempo = zone[i];
zone[i] = zone[j];
zone[j] = tempo;
i++, j--;
}
return zone;
}


Cela va-t-il réellement marcher ?
Si vous utilisez une archi x86, certainement. Disons que ainsi, ça

inverse (= corrige) une erreur de boutisme, sur une machine petit bout
en premier sur la base du char.

Il m'avait semblé que pour des dword les octets étaient échangés word
par word.
je ne suis pas très clair voici un exemple tel que j'avais compris la
chose :
0xAABBCCDD en big endian donnerait 0xCCDDAABB
Ça se trouve certainement, sur des archis n'ayant pas d'accès mémoire

naturel plus petit que le 16 bits word. Reste à voir la taille du char
sur une telle archi.
Mais pas en x86.

Ou je me trompe et il donne simplement 0xDDCCBBAA ?? (Dans ce cas
évidemment mon problème était trivial et je vous ai embêtés pour rien :)
C'est certainement le cas. Faites avec un éditeur hexa un fichier

sansnom.dat commençant pas:
0xAA, 0xBB, 0xCC, 0xDD, 0xA1, 0xB1, 0xC1, 0xD1, etc.
(ce qui peut représenter dans votre "network byte order" 0xAABBCCDD
(dword) suivi de 0xA1B1 (word) etc.

puis testez:

unsigned int i;
unsigned short s;
FILE * fichier;
fichier = fopen("sansnom.dat", "rb");
if(fichier == NULL)
{
fprintf(stderr, "Erreur ouverture fichier");
}
else
{
fread(&i, sizeof(int), 1, fichier);
printf("%08Xn", i);
AJUSTEBIG2LITTLE(i);
printf("%08Xn", i);
fread(&s, sizeof(short), 1, fichier);
printf("%04Xn", s);
AJUSTEBIG2LITTLE(s);
printf("%04Xn", s);
fclose(fichier);
}
--
Pierre


Avatar
Antoine Leca
En 41a1e102$0$5989$, Hamiral va escriure:
Je veux lire un fichier dans un format binaire [...] en "network byte
order", c'est-à-dire big endian si j'ai bien compris.


Oui.

j'ai donc pensé aux fonctions de la série htonl, nltoh etc.
[mais] elles ne permettent de traduire que des types entiers et non
des flottants.


Autrement dit, c'est pas bon.

Ce que tu veux, c'est optimiser ta routine de lecture de flottants.
Tu connais le format de ton fichier, c'est déjà cela. Mais connais-tu l'odre
(gros ou petit boutien) des flottants sur ta machine ? Parce qu'il se trouve
que ce n'est pas forcément le même que celui auquel tout le monde pense,
celui des entiers. :-)))


je vais être obligé d'écrire mes propres routines de conversions.


Voilà, tu as trouvé...
<X86>
Regarde quand même s'il n'y a pas une primitive MMX ou SSE qui fait cela
toute seule. Même si en terme de perfs le MMX va être bizarre, si tu
utilises du MMX tu ne peux plus utiliser le flottant x87 avec de bonnes
perfs, donc il te faut un compilo+processeur qui manipule les flottants en
SSE.
</X86>


Antoine

Avatar
Jean-Marc Bourguet
"Antoine Leca" writes:

Ce que tu veux, c'est optimiser ta routine de lecture de
flottants. Tu connais le format de ton fichier, c'est
déjà cela. Mais connais-tu l'odre (gros ou petit boutien)
des flottants sur ta machine ? Parce qu'il se trouve que
ce n'est pas forcément le même que celui auquel tout le
monde pense, celui des entiers. :-)))


Je ne suis pas sûr du tout qu'il n'y ait que deux formats de
flottants qui se réclame de la norme IEEE. Gaby devrait
pouvoir commenter, il me semble qu'il a touché à cela pour
gcc... Si j'ai raison petit/grand boutien IEEE n'est pas
une information suffisante pour connaitre le format.

A+

--
Jean-Marc
FAQ de fclc: http://www.isty-info.uvsq.fr/~rumeau/fclc
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
James Kanze
Hamiral writes:

|> > Hors bibliothèque spécifiquement orientée TCP/IP, vous êtes de
|> > toutes façons obligé de faire des tests/hypothèses.

|> Ce format fichier n'a rien à voir avec les réseaux, mais sa
|> documentation dit que les données numériques sont stockées en
|> "network byte order".

Sauf qu'il n'y a pas de « network byte order » pour les flottants.

En fin de compte, la question reste. Tu connais le format, c-à-d l'ordre
où se trouvent les bits, et ce que chaque bit signifie. Donc, on
principe, on peut le décoder. La seule question, c'est jusqu'où veut-on
pousser la portabilité. Si on la prend vraiment au sérieux : tu lis les
bits, dans l'ordre qu'ils se présentent. (En fait, tu lis par bloc de 8
bits, puis tu extrais les bits un à un.) Puis, tu les rassembles en
signe, exposant et mantissa. Et à la fin, tu les reassemble
« mathématiquement ».

C'est un sacré boulot. La plupart du temps, pour rien, au moins que tu
risques d'avoir à porter sur un gros IBM ou Unisys. Dans la pratique, si
tu sais que le fichier contient du IEEE, et que ton système aussi, tu
triches. Tu lis comme si c'était un type entier de la taille qui
convient, et puis quelque coups de cast, et le tour est joué. Donc, pour
un double, tu rassembles les octets en un unsigned long long. À peu près :

bool
readDouble( double* dest, FILE* source )
{
unsigned long long result = 0 ;
int shift = 64 ;
do {
int ch = fgetc( source ) ;
if ( ch != EOF ) {
shift -= 8 ;
result |= (unsigned long long)ch << shift ;
}
} while ( ch != EOF && shift != 0 ) ;
if ( ch != EOF ) {
dest = *(double*)( &result ) ;
}
return ch != EOF ;
}

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
James Kanze
Jean-Marc Bourguet writes:

|> "Antoine Leca" writes:

|> > Ce que tu veux, c'est optimiser ta routine de lecture de
|> > flottants. Tu connais le format de ton fichier, c'est
|> > déjà cela. Mais connais-tu l'odre (gros ou petit boutien)
|> > des flottants sur ta machine ? Parce qu'il se trouve que
|> > ce n'est pas forcément le même que celui auquel tout le
|> > monde pense, celui des entiers. :-)))

|> Je ne suis pas sûr du tout qu'il n'y ait que deux formats de
|> flottants qui se réclame de la norme IEEE. Gaby devrait
|> pouvoir commenter, il me semble qu'il a touché à cela pour
|> gcc... Si j'ai raison petit/grand boutien IEEE n'est pas
|> une information suffisante pour connaitre le format.

En théorie ou en pratique ?

En principe, la norme IEEE précise les bits et leurs signification. Elle
ne précise pas l'ordre, ni même la représentation physique -- je crois
qu'une implémentation pourait mélanger les bits de l'exposant en plein
milieu de la mantisse, et sans doute complémenter chaque troisième bits.
Et y ajouter un bit de parité, pourquoi pas.

Dans la pratique, je ne crois pas qu'on court beaucoup de risque. (Mais
je peux me trompé. Je me sers assez peu du flottant.)

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
Charlie Gordon
"James Kanze" wrote in message
news:

bool
readDouble( double* dest, FILE* source )
{
unsigned long long result = 0 ;
int shift = 64 ;
do {
int ch = fgetc( source ) ;
if ( ch != EOF ) {
shift -= 8 ;
result |= (unsigned long long)ch << shift ;
}
} while ( ch != EOF && shift != 0 ) ;
if ( ch != EOF ) {
dest = *(double*)( &result ) ;
}
return ch != EOF ;
}


Quelle coincidence : une boucle do/while et un gros bug dedans !
ch n'est pas testable dans le while(), ni après bien entendu.
D'ailleurs tester quatre fois ch == EOF en 10 lignes de code, c'est un peu
lourd.

bool readDouble(double *dest, FILE *source)
{
unsigned long long result = 0;
int ch, shift = 64;

while (shift > 0) {
ch = getc(source);
if (ch == EOF)
return FALSE;
shift -= 8;
result |= (unsigned long long)ch << shift;
}
dest = *(double*)&result;
return TRUE;
}

--
Chqrlie (on a crusade against do/while loops ;-)

1 2