2 votes

Le code ci-dessous est-il bien formé, en particulier en ce qui concerne les règles d'aliasing?

La fonction de modèle ci-dessous fait partie d'un générateur de séquences. Au lieu de décalages manuels, j'ai trouvé la solution basée sur les unions suivante pour rendre les opérations plus explicites. Ça fonctionne bien sur tous les compilateurs testés. Lien Godbolt.

Cependant, malgré son bon fonctionnement en pratique, je crains que les règles d'aliasing ne soient violées, ce qui signifie que cela pourrait ne pas fonctionner à l'avenir ou sur un autre compilateur que GCC et CLANG.

Strictement du point de vue de la norme C++ : le code ci-dessous est-il bien formé ? Est-ce qu'il entraîne un comportement indéfini ?

template 
uint64_t flog2(uint64_t num) {
    constexpr uint64_t MAXNUM = (uint64_t(1) << BITS);    
    if (num < MAXNUM) return num; 
    union FP {
        double dbl;
        struct {
            uint64_t man: 52;
            uint32_t exp: 11;
            uint32_t sign: 1;
        };
        struct {
            uint64_t xman: 52-BITS;
            uint32_t xexp: 11+BITS;
            uint32_t xsgn: 1;
        };
    };
    FP fp;
    fp.dbl = num;
    fp.exp -= 1023-1+BITS;
    return fp.xexp;
}

Merci !

3voto

user17732522 Points 943

Tout d'abord, le programme est syntaxiquement mal formé dans le standard ISO C++. Les membres de struct anonymes ne sont pas du C++ standard (contrairement au C). Ils représentent une extension. Dans le C++ standard ISO, la struct doit avoir un nom et être accédée à travers ce nom.

Je vais ignorer cela pour le reste de la réponse et prétendre que vous y accédiez à travers un tel nom.


Techniquement, ce n'est pas une violation d'aliasing, mais un comportement indéfini pour la lecture d'un membre inactif de l'objet union dans

fp.exp -= 1023-1+BITS;

Les types n'ont pas vraiment d'importance pour cela (contrairement à l'aliasing). Il n'y a toujours qu'au plus un membre actif d'une union, qui serait le dernier qui a été soit créé explicitement, soit écrit avec une expression d'accès/affectation de membre. Dans votre cas, fp.dbl = num; signifie que dbl est le membre actif et le seul qui peut être lu.

Il y a une exception dans le standard pour accéder à la séquence initiale commune des membres de type de classe de mise en page standard d'une union, auquel cas le non-actif peut être accédé comme s'il était actif. Mais même vos deux membres struct { ont une séquence initiale commune non vide uniquement pour BITS == 0.

Cependant, en pratique, les compilateurs supportent généralement explicitement ce type de tricherie de type, probablement déjà pour la compatibilité avec le C où c'est autorisé.


Évidemment, même en mettant tout cela de côté, la mise en page des champs de bits et les représentations des types impliqués sont complètement définies par l'implémentation et on ne peut pas s'attendre à ce que cela soit généralement portable.

1voto

nielsen Points 1201

Il est un comportement indéfini de lire à partir d'un membre de l'union qui n'a pas été écrit le plus récemment.

De plus, la disposition des champs de bits est définie par l'implémentation.

Par conséquent, du point de vue strict de la norme C++, ce code invoque à la fois un comportement indéfini (en lisant exp après avoir écrit dbl) et repose sur un comportement défini par l'implémentation en supposant que la disposition des champs de bits correspond à la représentation en virgule flottante du type double (qui soit dit en passant est également défini par l'implémentation).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X