Pourquoi en C ++ le static_cast <unsigned> des nombres négatifs diffère-t-il si le nombre est constant ou non

28

Quelles sont les règles C ++ qui signifient que l' égalité est fausse ?. Donné:

float f {-1.0};
bool equal = (static_cast<unsigned>(f) == static_cast<unsigned>(-1.0));

Par exemple https://godbolt.org/z/fcmx2P

#include <iostream>

int main() 
{
          float   f {-1.0};
    const float  cf {-1.0};

    std::cout << std::hex;
    std::cout << " f" << "=" << static_cast<unsigned>(f) << '\n';
    std::cout << "cf" << "=" << static_cast<unsigned>(cf) << '\n';

    return 0;
}

Produit la sortie suivante:

 f=ffffffff
cf=0
GreyMattR
la source
6
Ayez un vote positif: vous avez été pris par une règle souvent oubliée concernant un comportement indéfini!
Bathsheba
Quels résultats attendez-vous de la conversion d'un flottant négatif en un flottant non signé?
Amadeus
1
@Amadeus est probablement le bouclage habituel que nous obtenons lors de la conversion d'un entier négatif. J'ai dû vérifier que c'était UB car cela m'a surpris.
Programmeur
1
@Amadeus, il s'agissait plutôt de comprendre la différence. J'ai corrigé un bogue typo il y a quelques semaines ... un const-float a été explicitement casté en unsigned (le bogue), et implicitement de nouveau en signé (en tant que paramètre de fonction signé). Plus tard, j'ai réfléchi à la raison pour laquelle le bug d'origine provoquait une valeur nulle dans la fonction. Les tests suggèrent que c'était parce que le flotteur était constant. Un flotteur non-const qui a été explicitement converti en non signé puis implicitement restitué en signé n'a pas entraîné le même comportement - le non-const à deux cast avait la valeur d' origine et attendue.
GreyMattR

Réponses:

26

Le comportement de votre programme n'est pas défini : le standard C ++ ne définit pas la conversion d'un type à virgule flottante négatif à en un unsignedtype.

(Notez que le comportement de bouclage familier ne s'applique qu'aux négatifs types intégraux .)

Il est donc inutile de tenter d'expliquer la sortie de votre programme.

Bathsheba
la source
1
Est-il défini si je convertissais float-> int-> unsigned à la place?
Yksisarvinen
5
@Yksisarvinen: uniquement si le floatest dans la plage d'un int.
Bathsheba
J'accepte que UB soit la bonne réponse, et devrait donc être la fin ... mais étant donné que ... Quelle est la réponse probable du compilateur-écrivain qui explique pourquoi tous les compilateurs sur Compiler Explorer (clang / gcc / djgpp) produisent la sortie équivalente (UB)?
GreyMattR
5
@GreyMattR Si le compilateur peut prouver que la valeur est garantie négative au moment du cast, il peut laisser le résultat du cast non initialisé, ou le mettre à zéro, ou tout ce qu'il veut faire. Si le compilateur ne peut pas le prouver, il doit générer du code pour effectuer le transtypage. À de telles fins, il peut réutiliser le code à transtyper en type entier signé (le résultat ne sera "incorrect" que si le transtypage est UB, ce qui signifie qu'il n'est pas réellement faux). Avec une optimisation plus agressive, le cast ne sera pas non plus émis dans le cas non-const.
Brian
@Brian, merci pour cette explication utile.
GreyMattR