Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

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

10 réponses

1 2
Avatar
Fabien LE LEZ
On 22 Mar 2006 22:32:43 GMT, Michael
:

uchar tmp[ 4 ] ;


Pourquoi uchar ?

if (!source.read(reinterpret_cast<char*>(tmp),4))
throw Exception("Impossible de lire le fichier");

float * f = (float *)tmp;


Et pourquoi un cast à la C ici ?


Si tu veux lire un nombre dans un fichier de façon aussi brute que ça,
void* pourrait être pas mal. Ou bien char*, puisque c'est le type
attendu par istream::read.

static size_t const TAILLE_BUF= sizeof (float);
char buf[TAILLE_BUF];
source.read (buf, TAILLE_BUF); // + contrôle d'erreur

float reponse= *reinterpret_cast<float const*> (buf);
return reponse;


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?


C'est surtout la représentation d'un float en mémoire qui risque
d'être différente...

De toutes façons, si tu lis des nombres depuis un fichier de cette
façon, c'est fortement dépendant de la plate-forme.



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


Ça, c'est très simple.

unsigned int i; // Ou unsigned short, unsigned long, etc.

Le bit de poids faible est : i&1

Le bit de poids fort, c'est à peine plus compliqué :
i >> (sizeof(i)*CHAR_BIT-1)

Avatar
Fabien LE LEZ
J'ai osé écrire :

static size_t const TAILLE_BUF= sizeof (float);
char buf[TAILLE_BUF];
source.read (buf, TAILLE_BUF); // + contrôle d'erreur
float reponse= *reinterpret_cast<float const*> (buf);


Bien sûr, ce code ne peut fonctionner que si le fichier binaire a été
créé avec un code analogue, compilé avec le même compilo (ou un
compilo compatible).

Enfin bon, tout ça est bien casse-gueule. Bon débogage...

Avatar
Sylvain
Fabien LE LEZ wrote on 23/03/2006 00:33:
J'ai osé écrire :

static size_t const TAILLE_BUF= sizeof (float);
char buf[TAILLE_BUF];
source.read (buf, TAILLE_BUF); // + contrôle d'erreur
float reponse= *reinterpret_cast<float const*> (buf);


Bien sûr, ce code ne peut fonctionner que si le fichier binaire a été
créé avec un code analogue, compilé avec le même compilo (ou un
compilo compatible).


pourquoi donc ?

Sylvain.


Avatar
Marc Boyer
Le 22-03-2006, Fabien LE LEZ a écrit :
On 22 Mar 2006 22:32:43 GMT, Michael
:

uchar tmp[ 4 ] ;


Pourquoi uchar ?

if (!source.read(reinterpret_cast<char*>(tmp),4))
throw Exception("Impossible de lire le fichier");

float * f = (float *)tmp;


Et pourquoi un cast à la C ici ?


Si tu veux lire un nombre dans un fichier de façon aussi brute que ça,
void* pourrait être pas mal. Ou bien char*, puisque c'est le type
attendu par istream::read.

static size_t const TAILLE_BUF= sizeof (float);
char buf[TAILLE_BUF];
source.read (buf, TAILLE_BUF); // + contrôle d'erreur

float reponse= *reinterpret_cast<float const*> (buf);


Et si les contraintes d'alignement de float* ne sont
pas respectées par &buf, que ce passe-t-il ?
Pourquoi pas un tout bête
memcpy(&reponse, buf, sizefof(reponse) );

Marc Boyer
--
Si tu peux supporter d'entendre tes paroles
Travesties par des gueux pour exciter des sots
IF -- Rudyard Kipling (Trad. Paul Éluard)


Avatar
kanze
Michael wrote:

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

je dois extraire un flottant codé sur 4 octets depuis un
fichier binaire,


En quel format ? XDR ?

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 ?) ?

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.

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?


Certainement. Si tu ne connais pas la représentation des
flottants dans le fichier, tu es mal barré. Si tu le connais, il
faut lire les octets, en extraire la mantisse, l'exposant et la
signe, et les réassembler dans le format interne au coup de
ldexp et al.

J'ai du code qui le fait de façon portable pour XDR, c-à-d des
flottants IEEE sur quatre ou huit octets, avec la mantisse sur
les premiers octets, puis l'exposant, et la signe sur le bit du
poids fort du dernier octet. Ce qui correspond, je crois, au
format MIPS, et en inversant les octets, au format Intel. DEC
est légèrement différent : d'après mes souvenirs, les valeurs
sur quatre octets ne diffèrent d'Intel que par l'excès de
l'exposant, au moins pour les valeurs « normalisées » (et zéro).
Pour les flottants de huit octets, en revanche, c'est nettement
différent.

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.

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 ] ;
}


Ce qui suppose des shorts en format Intel, et non XDR.

--
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
kanze
Fabien LE LEZ wrote:
On 22 Mar 2006 22:32:43 GMT, Michael
:

uchar tmp[ 4 ] ;


Pourquoi uchar ?


Parce qu'il va les traiter au niveau des bits par la suite ?
Avec peut-être des décalages ?

Dans beaucoup de milieu, uchar est le type conventionnel pour
la mémoire brute. Des bits en quelque sorte.

if (!source.read(reinterpret_cast<char*>(tmp),4))
throw Exception("Impossible de lire le fichier");

float * f = (float *)tmp;


Et pourquoi un cast à la C ici ?


Pour masquer un reinterpret_cast, qui risque de provoquer un
core dump sur certaines machines (dont la mienne). Note bien
que rien ne garantit que tmp soit correctement aligné ; ici,
c'est probable parce que c'est la seule variable locale, mais en
ajoute une autre, et qui sait. Et même si c'est aligné, rien ne
garantit que les valeurs dans ces quatres octets ne
correspondent pas à une valeur interdite sur mon architecture.

Maintenant, si il se trouve sur une machine Intel 80x86, où des
erreurs d'alignement ne sont pas fatales (elles rallentissent
l'exécution seulement), et que les données qu'il lit ont été
écrit par un programme qui tournait lui aussi sur une machine
Intel 80x86, et que les deux ont été compilé avec un compilateur
rélativement récent, le code marchera comme prévu. Mais ça fait
beaucoup de « si », à mon avis.

Si tu veux lire un nombre dans un fichier de façon aussi brute
que ça, void* pourrait être pas mal.


Sauf qu'on ne peut pas définir des tableaux de void. Ce qui
manque un peu à C++, peut-être, c'est un type « byte » -- comme
j'ai dit, dans beaucoup de milieu, uchar y sert par convention.

Ou bien char*, puisque
c'est le type attendu par istream::read.


C'est parce que la distinction entre lire des caractères et lire
des multiplets binaires n'est pas bien nette dans la
bibliothèque standard. Donc, par exemple, son code suppose que
si le istream est réelement un ifstream (probablement le cas),
il a été ouvert avec mode binary, et qu'il a imbué le locale
"C".

static size_t const TAILLE_BUF= sizeof (float);
char buf[TAILLE_BUF];
source.read (buf, TAILLE_BUF); // + contrôle d'erreur


Ce qui ne résoud en rien le problème d'alignement. Et qui
suppose aussi que les flottants dans le fichier ont la même
taille et la même représentation que les flottants internes, ce
qui est loin d'être donné. (Plus tard, il a fait mention des
formats Intel, MIPS et DEC. Ce qui suggère fort que dans au
moins deux cas sur trois, le format des flottants dans le
fichier n'est pas identique au format interne -- si le code
tourne sur un IBM 390, dans aucun cas.)

float reponse= *reinterpret_cast<float const*> (buf);


Encore : en cas de problème d'alignement, sur ma machine, au
moins, ça fait boom. Même si on est 100% sûr que les formats
soient identiques, il faut se servir de memcpy. Le seul cas où
de tels conversions marchent, c'est si 1) le buffer a été alloué
dynamiquement, afin de garantir l'alignement, 2) tu lis le
fichier par blocs assez grand pour respecter tout alignement
dans le fichier même, et 3) le protocol a été conçu exprès de
façon à ce que toutes les données soient suffisamment alignées
pour ton processeur.

Note que ce dernier point est rarement rempli -- la plupart des
protocols (ou au moins XDR) ont été conçu à un époque où un
alignement de quatre suffisait pour tout les machines, alors
qu'aujourd'hui, il en faut souvent huit pour un double ou un
long.

return reponse;

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?


C'est surtout la représentation d'un float en mémoire qui
risque d'être différente...


Qui est différent ! DEC et Intel, je connais, bien que mes
souvenirs de DEC ne sont pas parfaits. (Je ne m'en suis plus
servi depuis au moins 1979.) Je crois, mais je suis loin d'en
être sûr, que le format MIPS est le même que celui du Sparc ; si
c'est le cas, je le connais très bien. Et il n'est pas pareil
aux autres.

Mais il me semble qu'on avait traité ce thème dans un fil il y a
peu de temps.

De toutes façons, si tu lis des nombres depuis un fichier de
cette façon, c'est fortement dépendant de la plate-forme.


Oui et non. Lire des nombres de cette façon suppose qu'ils ont
été écris de cette façon depuis la même plate-forme. Je le fais,
par exemple, dans des fichiers temporaires, que je rélis plus
tard dans la même programme ; je le fais aussi quand je passe
des données par une pipe à un autre programme sur la même
machine, développé en commun avec le premier (c-à-d donc compilé
et linké avec le même compilateur et les mêmes options). Dans
les deux cas, le code ne dépend pas de la plate-forme, et marche
aussi bien sous Linux sur PC que sous Solaris sur Sparc.

En revanche, sans changer de plate-forme... Sous MS-DOS et des
compilateurs Microsoft, ça ne marche pas forcément si le code
qui écrit et le code qui lit ont été compilé avec des versions
différentes du compilateur.

--
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
Mais il me semble qu'on avait traité ce thème dans un fil il y a
peu de temps.


Oui, par moi, ici


Avatar
Michael
Pardon, ici:

http://groups.google.com/group/fr.comp.lang.c++/browse_frm/thread/12ff0eb1a
39611c9/0fcfc7756ac7b2de?qÃD&rnum=1#0fcfc7756ac7b2de
Avatar
Michael
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.

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.

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. 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;
}


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?

unsigned int i; // Ou unsigned short, unsigned long, etc.

Le bit de poids faible est : i&1

Le bit de poids fort, c'est à peine plus compliqué :
i >> (sizeof(i)*CHAR_BIT-1)



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 ] ;
}


Ce qui suppose des shorts en format Intel, et non XDR.


C'est le cas.


Avatar
Michael
Ce que je cherche à faire se trouve ici:

http://www.c3d.org/HTML/3ddatafloatingpointformat.htm

Je lis les words 7 et 8 avec un ReadFloat adapté au fichier (Intel ou DEC).
Je veux convertir le flottant récupéré en entier signé (j'avais mal lu, il
doit pas être unsigned), pour récupérer deux valeurs, situées sur le bit de
poids fort et le bit de poids faible.

La première valeur pouvant ensuite être découpée en 8 valeurs (d'ailleurs
comment faire?) et la seconde indiquant un degré de précision, que je
voudrais récupérer.

J'ai ça pour le moment, mais je ne suis pas sûr que ce soit bon:

coords.x = file->ReadFloat();
coords.y = file->ReadFloat();
coords.z = file->ReadFloat();

int i = file->ReadFloat();

int pFaible = i & 1;
int pFort = i >> (sizeof(i)*CHAR_BIT-1);
1 2