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

C Discussion :

Union numérique et addition


Sujet :

C

  1. #1
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut Union numérique et addition
    Salut à toutes et à tous

    Mon très cher compilateur me met aujourd'hui au défi:
    Je voudrais créer une union capable de tenir tous types de valeurs numériques: 1, 2, 4 ou 8 bytes;

    Rien de très compliqué jusque là, une simple union suffit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef union {
        int8_t b;
        int16_t s;
        int32_t i;
        int64_t l;
    } cvalue;
    Je voudrais ensuite pouvoir facilement additionner des "cvalue" de tous types... un byte avec un int, un short avec un long...

    Seulement, sur mon architecture intel (little endian donc si je ne m'abuse... corrigez moi), c'est infaisable de cette manière:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    v1.b = 1;
    v2.l = 1;
     
    int64_t result = v1.l + v2.l;
    printf("Resultat: %li\n", result);
     
    // Résultat: 2686722
    Si je pouvais mettre ces valeurs en big endian et les additionner en mode big endian, ça résoudrait le souci... mais je ne pense pas que ce soit possible, ou même performant.

    Donc si je comprends bien, mon problème, précisément, est le suivant:
    Dans mon union, les membres sont alignés sur le début:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Une cvalue prend 8 bytes:
    |  |  |  |  |  |  |  |  |
     
    Un int8_t prend le premier byte, les autres sont sensé être à zero
    | 1| 0| 0| 0| 0| 0| 0| 0|
     
    Un int64_t prend tous les bytes
    | 0| 0| 0| 0| 0| 0| 0| 1|
    Du coup, l'addition foire.

    Je pense actuellement à adapter mon union avec des structures pour mettre du padding, comme ça les variables sont alignées, mais... oui, vous le voyez vous mêmes, ce n'est pas très propre.

    Quelqu'un aurait-il une meilleure idée ? Quelque chose qui fontionne aussi avec les soustractions, divisions et multiplications ?


    Edit: oh, dernière petite question: y a-t-il un moyen de savoir lors de la compilation si on est en little ou big endian ? style une constante du précompilateur ?

  2. #2
    Expert éminent sénior
    Avatar de diogene
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juin 2005
    Messages
    5 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 5 761
    Points : 13 926
    Points
    13 926
    Par défaut
    Je ne vois pas ce que vient faire la question de l'endian dans cette affaire.
    On peut parfaitement écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int64_t result = v1.l + v2.l;
    //ou encore, je suppose que c'est plutôt ce que tu voulais écrire
    int64_t result = v1.b + v2.l;
    La seule question qui se pose c'est que v1.b et v2.l doivent bien contenir un int8_t et un int64_t
    Normalement, pour pouvoir lire correctement la valeur sous la forme v1.l , il faut qu'auparavant on ait v1.l = .... et non quelque chose comme v1.s = .... . Et ce n'est pas lié à l'endian.
    Norme :
    6.5.2.3 Structure and union members
    ....
    82) If the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called "type punning"). This might be a trap representation.


    Un int8_t prend le premier byte, les autres sont sensé être à zero
    Qui garantit cela ?
    Norme :
    6.2.6.1 General
    ...
    7 When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values
    Publication : Concepts en C

    Mon avatar : Glenn Gould

    --------------------------------------------------------------------------
    Une réponse vous a été utile ? Remerciez son auteur en cliquant le pouce vert !

  3. #3
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Citation Envoyé par diogene Voir le message
    Je ne vois pas ce que vient faire la question de l'endian dans cette affaire.
    Parce qu'en big endian ce problème ne se poserait même pas.

    On peut parfaitement écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int64_t result = v1.l + v2.l;
    //ou encore, je suppose que c'est plutôt ce que tu voulais écrire
    int64_t result = v1.b + v2.l;
    Non, c'est bel et bien la première instruction que je veux écrire et faire fonctionner.

    Normalement, pour pouvoir lire correctement la valeur sous la forme v1.l , il faut qu'auparavant on ait v1.l = .... et non quelque chose comme v1.s = .... . Et ce n'est pas lié à l'endian.
    Si, justement. Essaye de refaire le schéma avec le big endian, et tout fonctionne bien.

    Qui garantit cela ?
    Mon programme garanti celà.


    Ce que je cherche, c'est justement de pouvoir faire des additions de manière simple, quel que soit le contenu de mes deux unions.

  4. #4
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    Parce qu'en big endian ce problème ne se poserait même pas.
    D'une manière générale, si le problème se pose également.
    Vu de la norme, écrire dans une des champs de l'union (b ici) et lire ensuite un autre champ (l ici) n'est pas correct [1].

    Cela peut fonctionner sur certaines architectures mais ce n'est pas garanti par la norme C et donc pas portable

    Citation Envoyé par Antoine_935 Voir le message
    Si, justement. Essaye de refaire le schéma avec le big endian, et tout fonctionne bien.
    Voir réponse précédente. Ce n'est pas garanti par la norme.

    Citation Envoyé par Antoine_935 Voir le message
    Mon programme garanti celà.
    La norme ne le garanti pas. Donc, sauf cas particulier bien précis, ce n'est pas correct.


    Bref, ce que tu cherches à coder peut fonctionner dans certains environnements mais n'est pas portable.

    Ceci étant, en oubliant pour l'instant l'utilisation d'union ou tout autre choix d'implémentation technique, que cherches-tu à faire précisément ?




    [1] Je laisse volontairement de côté les exceptions qui ne concernent pas du tout le cas présent.

  5. #5
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Citation Envoyé par gl Voir le message
    Cela peut fonctionner sur certaines architectures mais ce n'est pas garanti par la norme C et donc pas portable
    Bon, dommage, la solution sonnait plutôt pas mal.

    Ceci étant, en oubliant pour l'instant l'utilisation d'union ou tout autre choix d'implémentation technique, que cherches-tu à faire précisément ?
    Je cherche à faire une machine virtuelle. Non pas pour un os mais pour un langage interprété. C'est dans ce cadre que j'avais besoin de pouvoir additionner plusieurs types numériques entre eux sans me soucier de savoir ce qu'ils étaient réellement.

    Mais après un peu plus de recherches, je me suis rendu compte que j'opérais à trop haut niveau. C'est à dire que mon bytecode décrivait des opérations trop complexes. En redescendant plus bas, ce problème ne se pose même plus, je suis bel et bien obligé de faire mes conversions plus convenablement.


    Merci à vous deux pour les précisions sur ces normes

  6. #6
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    Edit: oh, dernière petite question: y a-t-il un moyen de savoir lors de la compilation si on est en little ou big endian ? style une constante du précompilateur ?
    Usuellement, non. Après, ça se teste facilement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int IsBigEndian ( void ) {
      int test = 0x11223344 ;
      char* p = (char*)(&test) ;
      return ((*p)==0x11) ;
    }
    Renvoie non-zéro si la plate-forme est en big-endian.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  7. #7
    Membre émérite Avatar de nicolas.sitbon
    Profil pro
    Inscrit en
    Août 2007
    Messages
    2 015
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 2 015
    Points : 2 280
    Points
    2 280
    Par défaut
    Citation Envoyé par Mac LAK Voir le message
    Usuellement, non. Après, ça se teste facilement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int IsBigEndian ( void ) {
      int test = 0x11223344 ;
      char* p = (char*)(&test) ;
      return ((*p)==0x11) ;
    }
    Renvoie non-zéro si la plate-forme est en big-endian.
    Ce code ne fonctionnera que sur des architectures ou un char fait 8 bits (et accessoirement ou int fait 32 bits (sans padding).
    On peut empêcher un comportement ératique en modifiant le type de p
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int IsBigEndian(void) 
    {
       uint32_t test = 0x11223344;
       uint8_t const * p = (uint8_t *) &test;
       return  (*p) == 0x11;
    }
    L'avantage, est que si ces types ne sont pas définis, on peut les réajuster avant de re-compiler suivant l'architecture sans craindre que la fonction ne retourne un résultat erroné.
    "The quieter you become, the more you are able to hear"
    "Plus vous êtes silencieux, plus vous êtes capable d'entendre"

  8. #8
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par nicolas.sitbon Voir le message
    Ce code ne fonctionnera que sur des architectures ou un char fait 8 bits (et accessoirement ou int fait 32 bits (sans padding).
    Sur ce genre de plate-forme (habituellement, c'est de l'embarqué ou du "dinosaure", ça devient de plus en plus rare), il est plus courant d'utiliser un système d'abstraction qui va, entre autres, définir des macros d'endianness.
    Si la cible est suffisamment "puissante" pour supporter une librairie d'abstraction complète (ACE, POCO), on utilise alors l'API d'abstraction directement. Sinon, ça se règle en général via un header gavé de compilation conditionnelle qui finit par assurer la définition d'une des deux macros "BIG_ENDIAN" / "LITTLE_ENDIAN".
    On peut trouver un tel header à cette adresse, par exemple, le seul souci étant qu'il marche rarement pour les cibles réellement exotiques (ex : cœur PPC embarqué dans un FPGA).

    Citation Envoyé par nicolas.sitbon Voir le message
    L'avantage, est que si ces types ne sont pas définis, on peut les réajuster avant de re-compiler suivant l'architecture sans craindre que la fonction ne retourne un résultat erroné.
    D'un autre côté, un problème d'endianness se voit immédiatement, dès le premier test unitaire... Test que tout développeur est censé faire.

    Quitte à devoir définir quelque chose pour faire compiler le code, autant passer directement une des deux macros d'endianness à la compilation, ça évite d'avoir à appeler du code...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #ifdef BIG_ENDIAN
      #ifdef LITTLE_ENDIAN
        #error Must not define BIG_ENDIAN *AND* LITTLE_ENDIAN.
      #else
        // OK.
      #endif
    #else
      #ifdef LITTLE_ENDIAN
        // OK.
      #else
        #error Must define BIG_ENDIAN or LITTLE_ENDIAN.
      #endif
    #endif
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  9. #9
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Le problème, c'est que je ne connais aucun moyen de vérifier à la compilation que ces macros ne sont pas incorrectement définies.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  10. #10
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Le problème, c'est que je ne connais aucun moyen de vérifier à la compilation que ces macros ne sont pas incorrectement définies.
    Moi non plus, c'est réellement un problème de test unitaire de portage / non-régression...

    En général, quand j'ai un truc de ce genre, je fais un petit programme de test très basique qui va tester des éléments comme :
    • Taille des structures après compilation (par rapport à une taille attendue).
    • Distance (en octets) entre les différents champs des structures en question.
    • Taille des mots-machine et des différents types de base.
    • Vérification de l'endianness (ntoh* et hton* peuvent permettre de vérifier ça).
    • Vérification des algos de base du système (ceux rajoutés pour les besoins du projet).
    • Etc.
    Ainsi, le lancement de ce petit test me sert de TU de portage, et en plus de test rapide de non-régression lors d'ajout de fonctionnalités.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  11. #11
    Membre éprouvé
    Avatar de Antoine_935
    Profil pro
    Développeur web/mobile
    Inscrit en
    Juillet 2006
    Messages
    883
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur web/mobile

    Informations forums :
    Inscription : Juillet 2006
    Messages : 883
    Points : 1 066
    Points
    1 066
    Par défaut
    Merci pour ces réponses supplémentaires
    Je garderai précieusement ce petit header pour les macros. Je ne pense pas être confronté bientôt à des systèmes aussi étranges que celui que tu décris, donc je peux sans doute utiliser ce header de manière assez sure

  12. #12
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Citation Envoyé par Antoine_935 Voir le message
    donc je peux sans doute utiliser ce header de manière assez sure
    Si tu ne bosses qu'avec des machines "classiques" (PC x86 sous Windows/Linux ou Mac), t'es certain d'être tranquille avec. Sinon, il peut y avoir des failles, mais le petit bout de #ifdef que j'ai mis un peu plus haut te permettra de toujours "blinder" en t'assurant qu'une seule et unique macro d'endianness est définie.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

Discussions similaires

  1. [AC-2010] Requête union; problème avec les champs numériques
    Par Fransous1 dans le forum Requêtes et SQL.
    Réponses: 6
    Dernier message: 06/02/2014, 20h48
  2. Créer une vue pour trier une requete UNION ?
    Par Etienne Bar dans le forum SQL
    Réponses: 3
    Dernier message: 03/01/2003, 20h22
  3. [Delphi 6] EditBox -> valeurs numériques ?
    Par JBrek dans le forum Composants VCL
    Réponses: 9
    Dernier message: 02/12/2002, 13h08
  4. [VB6] Problème d'addition de dates et de nombres
    Par pepper dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 28/11/2002, 21h12
  5. [imprecis]Réaliser a^n avec seulement l'opérateur d'addition
    Par Amon dans le forum Algorithmes et structures de données
    Réponses: 18
    Dernier message: 08/11/2002, 22h22

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