IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

reinterpreter_cast -> warning


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 14
    Par défaut reinterpreter_cast -> warning
    Bonjour,

    Je suis en train d'implémenter le radix sort en C++, et j'aimerais bien l'implémenter pour trier des nombres flottants.
    Ce n'est pas une question d'algorithmie que je vous demande, mais j'ai plutôt un problème avec le langage.

    Pour trier les float, je dois lire les valeurs de chaque bit du float (L'algorithme fonctionnera pour les float enregistrés selon la norme IEEE). Je voulais donc utiliser les opérateurs bitwise (& logique), mais le langage interdit de faire un & entre un float et un int (3.f & 1 est interdit).

    La solution que j'ai implémentée me retourne un warning que je ne comprends pas.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    float f = /* something */;
    int a = *reinterpreter_cast<int*>(&f);
    De cette façon là, je récupère les bits de "f" et je peux les lire via "a". Lorsque ce code s'exécute, il a déjà été vérifié que sizeof(int) == sizeof(float) (Si la taille est différente, l'algorithme sera faux, mais est-ce que ça peut mener à un crash du programme ?).
    Le problème de cette méthode est que j'obtiens un warning :
    warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing] (GCC 4.6.x)
    Je ne comprends pas que ça veut dire. Néanmoins, si je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    float f = /* something */;
    int* pa = reinterpreter_cast<int*>(&f);
    int a = *pa;
    Le warning disparait. (Pourtant les deux codes ont l'air équivalents...)

    Ma première question est : y a-t-il un moyen simple de contourner la limitation (& logique interdit entre float et int) ?
    Ensuite : ma solution est-elle dangereuse ? (sinon laquelle préférer entre les deux ?)

  2. #2
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Je te conseille la lecture de https://svn.boost.org/trac/boost/wik...ingsGuidelines (ça commence en milieu de page) qui décrit bien le problème, sa cause, et des solutions.

    Ton code n'est formellement pas correct. Rien ne garanti que la valeur dans le int est une valeur valide pour un int. Seul les trois types char ont la garanti que tous leurs bits ont du sens. En pratique, je pense que ton code ne pose pas de soucis particuliers, et tu fais déjà d'autres suppositions sur la représentation des flaots qui me semblent encore plus risquées. Par contre, par principe, pour manipuler des bits, j'aurais plutôt tendance à utiliser des types unsigned, et en particulier chars.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  3. #3
    Membre averti
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 14
    Par défaut
    Ton code n'est formellement pas correct. Rien ne garanti que la valeur dans le int est une valeur valide pour un int. Seul les trois types char ont la garanti que tous leurs bits ont du sens.
    Il me semblait bien qu'il y avait un truc comme ça, d'où ma question^^.

    tu fais déjà d'autres suppositions sur la représentation des flaots qui me semblent encore plus risquées.
    Mouais, je ne sais pas à quel point la norme IEEE est suivi, mais je n'ai pas d'autres choix de toute façon. Il y a une norme plus suivie ? (Ca peut paraitre étrange, mais l'algorithme se fiche bien de savoir sur combien de bits sont codés l'exposant et la mantisse, donc si ces données changent, ça n'a aucune conséquence).

    Je te conseille la lecture de https://svn.boost.org/trac/boost/wik...ingsGuidelines (ça commence en milieu de page) qui décrit bien le problème, sa cause, et des solutions.
    Merci! C'est ce qu'il me fallait! Je ne pensais plus aux unions.

    La solution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    union {
    	float f;
    	int i;
    } data;
    data.f = /* some float */;
    data.i; // Access to the integer representation;
    Merci encore!

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Salut,
    Comme c'est rappelé dans la doc boost proposée par Loic, l'utilisation de l'union n'est pas garantie en C++ (on n'a pas de garantie lorsqu'on accède à un autre membre que celui initialisé).

  5. #5
    Membre Expert
    Avatar de Joel F
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    Septembre 2002
    Messages
    918
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique
    Secteur : Service public

    Informations forums :
    Inscription : Septembre 2002
    Messages : 918
    Par défaut
    Pour info:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    float f = 1.f;
    boost::int32_t i;
    char* pf = &f;
    char* pi = &i;
     
    memcpy(pi, pf, siozef(float));
    Fait exactement ce qu'il faut, ne fait pas de warning *et* est optimisé sur tout les compilos par un simple MOV.

  6. #6
    Membre averti
    Profil pro
    Étudiant
    Inscrit en
    Janvier 2012
    Messages
    14
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Janvier 2012
    Messages : 14
    Par défaut
    Ah, j'ai lu trop vite...

    Comme c'est rappelé dans la doc boost proposée par Loic, l'utilisation de l'union n'est pas garantie en C++ (on n'a pas de garantie lorsqu'on accède à un autre membre que celui initialisé).
    Je ne comprends pourquoi. Tant que float et int ont la même taille. L'intérêt de l'union est justement qu'ils partagent la même mémoire. (je suppose que c'est encore un problème d'alignement^^)

    Je trouve que c'est quand même assez décevant que l'on doive passer par des mécanismes aussi "complexes" pour simplement lire les bits d'un float...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    float f = 1.f;
    boost::int32_t i;
    char* pf = &f;
    char* pi = &i;
     
    memcpy(pi, pf, siozef(float));
    Dans le site, il est marqué que l'appel à memcpy est optimisé, mais est-ce aussi rapide que les solutions que j'ai proposé auparavant (union ou reinterpreter_cast).
    La vitesse d'exécution est tout de même importante (ce code sera exécuté n*k fois, où n est la taille du tableau à trier, k la taille en bit d'un float.)

    Je viens de penser à une autre solution :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    float f = /* something */;
    int i = *(new (&f) int);
    Je n'ai jamais utilisé l'opérateur new (*), donc je ne sais pas ce que ça vaut.

    La solution de JoelF me semble bien, je l'adopterais s'il n'y a pas de solutions plus simples/rapides. (merci!)

    Merci encore de vos réponses.

Discussions similaires

  1. un warning
    Par isidore dans le forum C
    Réponses: 6
    Dernier message: 14/04/2004, 12h25
  2. Un warning devient fatal
    Par tomnie dans le forum Linux
    Réponses: 3
    Dernier message: 01/04/2004, 13h48
  3. [langage] Récupérer des Warning ??
    Par armada dans le forum Langage
    Réponses: 2
    Dernier message: 05/06/2003, 16h45
  4. [warning][properties]problème de police introuvable
    Par cyrdec dans le forum API standards et tierces
    Réponses: 8
    Dernier message: 11/04/2003, 17h41
  5. Warnings lors de la compilation
    Par polo54 dans le forum C
    Réponses: 5
    Dernier message: 07/02/2003, 09h12

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo