OVH Cloud OVH Cloud

float => unsigned int et extraction bits poids fort

13 réponses
Avatar
Michael
Bonsoir à tous,

voilà ce que je n'arrive pas à faire:

je dois extraire un flottant codé sur 4 octets depuis un fichier binaire,
que je dois convertir en entier non signé.

Ensuite je dois extraire les bits de poids fort et faible et récupérer
ces deux valeurs...

Récupérer le flottant, j'y arrive:

float IntelDataHandler::readFloat(std::ifstream & source ) const
{
uchar tmp[ 4 ] ;
if (!source.read(reinterpret_cast<char*>(tmp),4))
throw Exception("Impossible de lire le fichier");

float * f = (float *)tmp;
float flt = *f;

return flt;
}

Mais pour le reste? Est-ce que la conversion et l'extraction des bits est
différente selon qu'on soit sur des représentations intel, MIPS ou DEC?

Et comment je peux extraire les deux données?

Est-ce que je ne peux tout simplement pas, au lieu de lire un float, lire
deux short, me donnant directement les bonnes valeurs?

short IntelDataHandler::readShort(std::ifstream & source ) const
{
uchar tmp[ 2 ] ;
if (!source.read(reinterpret_cast<char*>(tmp),2))
throw Exception("Impossible de lire le fichier");

return (tmp[ 1 ] << 8) | tmp[ 0 ] ;
}

Merci d'avance

Mike

3 réponses

1 2
Avatar
kanze
Michael wrote:
je dois extraire un flottant codé sur 4 octets depuis un
fichier binaire,


En quel format ? XDR ?


Intel ou DEC. J'ai déjà les routines d'extraction de ce
flottant, que tu m'avais donné dans un fil précédent.


Il me semble qu'il y avait du déjà vu ici:-).

que je dois convertir en entier non signé.


Avec quelles règles d'arrondi ? Où est-ce qu'on sait qu'a
priori, le flottant représente un entier dans l'intervale
représentable dans le type cible (int, long ?) ?


Le flottant représente un entier, positif de surcroit.


Et avec une valeur pas trop grande, j'espère. Convertir 1e300 en
entier, ça ne serait pas de la tarte.

Si on est sûr que ça ne déborde pas, la conversion est fournie
par le langage : static_cast< int >( unFloat ).

Ensuite je dois extraire les bits de poids fort et faible
et récupérer ces deux valeurs...


Je ne suis pas sur ce que tu veux dire par là. A priori, si
tu as un int32_t, « ((uint32_t)(-1) << (32 - n)) & valeur »
récupère les n bits de poids forts. Alternativement, on
pourrait vouloir les avoir comme valeur, avec simplement «
valeur >> (32 - n) » (mais attention au comportement en cas
de valeur négative).

Récupérer le flottant, j'y arrive:

float IntelDataHandler::readFloat(std::ifstream & source ) const
{
uchar tmp[ 4 ] ;
if (!source.read(reinterpret_cast<char*>(tmp),4))
throw Exception("Impossible de lire le fichier");

float * f = (float *)tmp;
float flt = *f;

return flt;
}


Ça ne marche que si le flottant a été écrit par un programme
compilé avec la même version du même compilateur, compilé
avec les mêmes options, pour la même architecture. Et
encore... rien ne garantit que tmp soit correctement aligné
pour contenir un float.


J'ai testé avec plusieurs fichiers écrits par des machines x86
différentes, et ça marche tout le temps.


Qui l'a écrit n'est pas forcément la question -- on suppose
qu'ils ont pris les précautions pour l'écrire correctement. La
question est : où on le lit. Ce code ne fonctionnera sûrement
pas sur ma machine (un Sun Sparc, sous Solaris).

Dans le cas de fichiers DEC, j'ai une autre routine adaptée,
que tu m'avais donné aussi je crois bien ;)

float DECDataHandler::readFloat(std::ifstream & source ) const
{
uchar tmp[ 4 ] ;
if (!source.read(reinterpret_cast<char*>(tmp),4))
throw Exception("Impossible de lire le fichier");

long mantis = (tmp[2] & 0xff) + ((tmp[3] & 0xffl) << 8) + (((tmp[0] &
0x7fl) | 0x80) << 16);
short exp = (tmp[1] & 0x7f) * 2 + ((tmp[0] & 0x80) ? 1 : 0);
if (tmp[1] & 0x80)
mantis = -mantis;

float flt = (mantis || exp) ? ((float) ldexp (mantis, (exp - 128 - 24)))
: 0;

return flt;
}


Personnellement, je ferais quelque chose de semblable pour
Intel. Pour le cas où je dois le compiler sur un processeur
autre qu'un 80x86. Il suffit en gros de changer les indices des
octets, et de soustraire 126 à la place de 128 de l'exposant.
Mais peut-être il faudrait aussi ajouter du code pour traiter
des dénormalisés, des infinités et des NaN.

Et comment je peux extraire les deux données?

Est-ce que je ne peux tout simplement pas, au lieu de lire un
float, lire deux short, me donnant directement les bonnes
valeurs?


Certainement pas.


Est-ce que du coup, comme les float que je récupère via les
routines que j'ai données plus haut sont corrects, je peux
utiliser le code suivant donné par Fabien plus haut?


Si tu ne veux qu'un seul bit chaque fois, oui. Mais d'après ton
autre posting, et la description du protocol, je crois en fait
que ce que tu veux, c'est de convertir le flottant en short, et
puis prends les deux octets. Seulement, la description du
protocol n'est pas des plus claires : que signifie ici « first
byte » et « second byte » ? J'ai plutôt l'impression que ça
aussi dépend de l'encodage, et serait différent pour MIPS que
pour Intel et DEC (qui utilise le même format pour des short).
Pour Intel et DEC, on aurait :

short tmp = readFloat() ;
uchar camera = tmp & 0xFF ;
uchar residual = tmp >> 8 ;

Pour MIPS, inverser camera et scale.

Mais c'est encore assez vague à mon avis. Est-ce que scale est
signé ou non ? Et d'autre texte dans le document suggère que
peut-être byte 1 signifie l'octet de poids fort, et non le
premier octet. Sauf que dans ce cas-là, normalement, on
parlerait de byte 0 pour l'octet de poids faible. Au moins qu'on
a appris l'informatique chez IBM.

(Je ne sais pas qui a inventé ce format, mais il a l'air d'être
un des plus tordu que je n'ai jamais rencontré. À part,
peut-être, des virgules flottants en BER.)

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



Avatar
Michael
Il me semble qu'il y avait du déjà vu ici:-).


En effet ;)

Et avec une valeur pas trop grande, j'espère. Convertir 1e300 en
entier, ça ne serait pas de la tarte.


Non ce sont des "petites" valeurs, pas de soucis avec ça.

Si on est sûr que ça ne déborde pas, la conversion est fournie
par le langage : static_cast< int >( unFloat ).


C'est ce que j'avais utilisé après coup.

Qui l'a écrit n'est pas forcément la question -- on suppose
qu'ils ont pris les précautions pour l'écrire correctement. La
question est : où on le lit. Ce code ne fonctionnera sûrement
pas sur ma machine (un Sun Sparc, sous Solaris).


C'est un programme destiné à tourner sur Windows uniquement.

Personnellement, je ferais quelque chose de semblable pour
Intel. Pour le cas où je dois le compiler sur un processeur
autre qu'un 80x86. Il suffit en gros de changer les indices des
octets, et de soustraire 126 à la place de 128 de l'exposant.
Mais peut-être il faudrait aussi ajouter du code pour traiter
des dénormalisés, des infinités et des NaN.


Tu m'en avais déjà parlé la dernière fois, et je n'ai pas encore eu le
temps de me pencher là dessus.

Si tu ne veux qu'un seul bit chaque fois, oui. Mais d'après ton
autre posting, et la description du protocol, je crois en fait
que ce que tu veux, c'est de convertir le flottant en short, et
puis prends les deux octets. Seulement, la description du
protocol n'est pas des plus claires : que signifie ici « first
byte » et « second byte » ? J'ai plutôt l'impression que ça
aussi dépend de l'encodage, et serait différent pour MIPS que
pour Intel et DEC (qui utilise le même format pour des short).
Pour Intel et DEC, on aurait :

short tmp = readFloat() ;
uchar camera = tmp & 0xFF ;
uchar residual = tmp >> 8 ;


Ce code marche parfaitement, c'est exactement ça qu'il me faut...

Mais c'est encore assez vague à mon avis. Est-ce que scale est
signé ou non ? Et d'autre texte dans le document suggère que
peut-être byte 1 signifie l'octet de poids fort, et non le
premier octet. Sauf que dans ce cas-là, normalement, on
parlerait de byte 0 pour l'octet de poids faible. Au moins qu'on
a appris l'informatique chez IBM.

(Je ne sais pas qui a inventé ce format, mais il a l'air d'être
un des plus tordu que je n'ai jamais rencontré. À part,
peut-être, des virgules flottants en BER.)


Je confirme. Pour la première fois où je dois lire dans un fichier
binaire, j'ai été gaté!

Encore merci pour vos lumières!

Avatar
kanze
Michael wrote:

[...]
Qui l'a écrit n'est pas forcément la question -- on suppose
qu'ils ont pris les précautions pour l'écrire correctement. La
question est : où on le lit. Ce code ne fonctionnera sûrement
pas sur ma machine (un Sun Sparc, sous Solaris).


C'est un programme destiné à tourner sur Windows uniquement.


Pour l'instant, en tout cas:-).

Mon expérience montre que ce genre de choses à une tendance à
évoluer. Dans la mesure où il existe une solution portable, je
le préfère.

Personnellement, je ferais quelque chose de semblable pour
Intel. Pour le cas où je dois le compiler sur un processeur
autre qu'un 80x86. Il suffit en gros de changer les indices des
octets, et de soustraire 126 à la place de 128 de l'exposant.
Mais peut-être il faudrait aussi ajouter du code pour traiter
des dénormalisés, des infinités et des NaN.


Tu m'en avais déjà parlé la dernière fois, et je n'ai pas encore eu le
temps de me pencher là dessus.


Il y a des chances que ça n'a pas d'importance. Beaucoup dépend
d'où vient les données.

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


1 2