0
votes

0/0 en C - GCC-7 ou supérieur

mathématiquement, 0/0 est indéfini. Mais, en programmation C (en particulier gcc compilateur GCC-7.3.0 sur Ubuntu), il produit la réponse comme 1 . J'aimerais connaître la raison. Travaille-t-il par soustraction répétée? Pour, le même code, si j'utilise n = 1 / n , je reçois une exception de point flottante. Cependant, pour GCC-5.3.0, il produit une erreur. xxx


12 commentaires

Comportement non défini (division de 0) est indéfini.


Mais, ni produit une erreur de compilation, ni aucune erreur d'exécution dans GCC> 7


@Panch: Un comportement non défini (par la norme C) ne signifie pas et que l'erreur (statique ou à l'exécution) doit être générée. Cela pourrait également signifier qu'un nombre aléatoire est renvoyé (1 dans ce cas). Le compilateur le fait probablement en cours d'optimisation de N / N et de le remplacer par 1.


Vous pouvez consulter ce code dans JDOODLE.COM/C-ONLINE-COMPILER , GCC- 8.1.0 ou 7.2.0


Faire des analyses de flux de contrôle intégral d'un programme est dure et laissé aux analyseurs statiques. Le compilateur génère un code exécutable de votre source sous l'hypothèse qu'il n'y a pas de comportement non défini. S'il y a, alors tous les paris sont éteints.


@Panch j'ai eu une erreur d'exécution ici avec un code légèrement différent. Encore une fois, il est indéfini. Vous pouvez obtenir n'importe quel effet, y compris aucune erreur sur tout ou partie des plates-formes.


Ensuite, dans un grand programme, s'il y a 0/0 et retourne un nombre aléatoire, comment un programmeur peut le déboguer, sans connaître sa valeur explicite.


En veillant à ce que le dénominateur n'est pas 0 avant faire une division. ( si (n! = 0) a = 5 / N; par exemple)


Pour 1 / N, vous ne recevez probablement pas d'exception ponctuelle flottante, mais une division par zéro exception (qui est réservée aux entiers uniquement). Point flottant Math est (sur pratiquement toutes les plateformes) algébriquement fermés, cela signifie que toutes les opérations génèrent une sortie valide. (D'où il y a des symboles supplémentaires INF, NAN et SIGNÉ ZERO)


@Panch GCC et CLANG ont -Fsanitize = non défini qui capte la division par zéro: coliru.staced-crooked.com/a/1ef496f6f34b032c


@Andreash. Eh bien, pas vraiment: en.wikipedia.org/wiki/signal_(IPC)#ssigfpe


@Bob__ Ces exceptions sont facultatives et désactivées par défaut. Je n'ai vu aucun code qui utilise réellement des exceptions FP. Toujours toutes les opérations FP génèrent une sortie valide, il n'y a pas de cas où une manipulation d'exception FP est nécessaire, car c'est le cas pour la division par zéro dans le boîtier entier


3 Réponses :


4
votes

Diviser par zéro est un comportement indéfini dans la programmation C (C11 6.5.5 §6). Cela signifie qu'il n'y a pas de résultat prévisible, il n'ya pas de dire ce que votre programme pourrait faire.

Il n'est pas très significatif d'essayer de raisonner pourquoi vous obtenez un résultat particulier de "tout ce qui pourrait arriver". Il pourrait imprimer 1, il pourrait imprimer 42, cela pourrait imprimer du tout. Le programme pourrait se bloquer et brûler. Si vous exécutez le programme deux fois, vous pourriez obtenir différents résultats. Et ainsi de suite.

Quant à la raison pour laquelle vous n'obtiendrez pas d'erreur de compilateur, le compilateur n'est ni en mesure de trouver ou de diagnostiquer des erreurs de temps d'exécution. La plupart des cas de comportement non défini doivent être évités par le programmeur.

Certains bons compilateurs peuvent être capables de donner des avertissements si vous utilisez des expressions de constante entière pure telles que int x = 0/0; , mais ce n'est à nouveau pas le travail du compilateur de trouver ce bug . C'est le travail du programmeur. Par conséquent, vous devez avoir l'habitude de vérifier toujours si le bon opérand de / ou % est zéro avant d'appliquer les opérandes.


0 commentaires

2
votes

Les compilateurs sont autorisés à supposer que le code n'invoque pas un comportement non défini et utilise cette hypothèse lors de l'optimisation du code.

Pour toute valeur, autre que n == 0 L'expression n / n est évaluée comme 1. Puisque la plongée par zéro est un comportement indéfini en C, le compilateur peut supposer que ce scénario n'arrive jamais, c'est-à-dire qu'il peut supposer que n! = 0 . Comme il peut supposer que le compilateur peut remplacer le lent n / n avec la valeur constante bon marché de 1 . C'est probablement ce qui se passe avec le code en question.

Bien sûr, le compilateur n'est pas tenu de faire une telle hypothèse et pourrait générer une division réelle. Selon le matériel qui exécute le code, un signal matériel spécifique (ou une exception) pourrait être déclenché dans un tel cas de division par zéro. Je doute que tout compilateur raisonnable émettrait la division dans cet exemple pour Code optimisé , car la division est extrêmement lente et, selon la norme, le 1 est suffisant. Cependant, il est toujours probable qu'il produira une division réelle en mode débogage.

Les choses similaires se produisent lors de l'optimisation d'un autre comportement non défini, tel que int débordement: < Pré> xxx

Ne comptez pas sur un comportement indéfini - il n'est pas prévisible.


2 commentaires

De même, le compilateur est libre de passer une fracture par zéro au code de la machine, auquel cas vous obtiendriez une exception spécifique à la CPU.


@Lundin, bien sûr, il peut émettre une exception spécifique à la CPU, et cela fait probablement cela en mode de débogage sur de nombreux systèmes. J'ai mis à jour la réponse en conséquence.



2
votes

Comme on peut le voir sur GODBOLT'S Compiler Explorer , GCC et clang optimiser n / n pour n entier pour évaluer à 1 sans aucun avertissement. gcc exécute cette optimisation même à -O0 alors que clang génère le fonctionnement de la division pour -O0 mais l'alternative optimisée pour -O1 et ci-dessus.

0/0 a un comportement non défini quand même, tout comportement de n / n est ok si n est zéro, y compris l'évaluation de < code> 1 sans effet secondaire observable.

Si vous insistez, vous pouvez qualifier n comme volatile et le compilateur peut générer une division, comme gcc et clang Dans ce cas, mais aussi longtemps que n est lu deux fois, il n'est pas nécessaire.


0 commentaires