Je sais, la question semble étrange. Les programmeurs pensent parfois trop. Veuillez lire la suite ...
En CI, utilisez signed
et unsigned
entiers beaucoup. J'aime le fait que le compilateur me prévienne si je fais des choses comme assigner un entier signé à une variable non signée. Je reçois des avertissements si je compare des entiers signés avec des entiers non signés et bien plus encore.
J'aime ces avertissements. Ils m'aident à garder mon code correct.
Pourquoi n'avons-nous pas le même luxe pour les flotteurs? Une racine carrée ne renverra certainement jamais un nombre négatif. Il existe également d'autres endroits où une valeur flottante négative n'a aucune signification. Candidat parfait pour un flotteur non signé.
Btw - Je ne suis pas vraiment intéressé par le seul petit plus de précision que je pourrais obtenir en supprimant le bit de signe des flotteurs. Je suis super content des float
s tels qu'ils sont en ce moment. Je voudrais juste marquer un flottant comme non signé parfois et obtenir le même genre d'avertissements que je reçois avec des entiers.
Je ne connais aucun langage de programmation prenant en charge les nombres à virgule flottante non signés.
Une idée de pourquoi ils n'existent pas?
ÉDITER:
Je sais que le FPU x87 n'a pas d'instructions pour traiter les flotteurs non signés. Utilisons simplement les instructions de flotteur signées. Une mauvaise utilisation (par exemple, passer en dessous de zéro) pourrait être considérée comme un comportement indéfini de la même manière qu'un débordement d'entiers signés n'est pas défini.
Réponses:
La raison pour laquelle C ++ ne prend pas en charge les flottants non signés est qu'il n'y a pas d'opérations de code machine équivalentes que le CPU doit exécuter. Il serait donc très inefficace de l'appuyer.
Si C ++ le supportait, alors vous utiliseriez parfois un flottant non signé sans vous rendre compte que votre performance vient d'être supprimée. Si C ++ le supportait, chaque opération en virgule flottante devrait être vérifiée pour voir si elle est signée ou non. Et pour les programmes qui effectuent des millions d'opérations en virgule flottante, ce n'est pas acceptable.
La question serait donc de savoir pourquoi les implémenteurs de matériel ne le prennent pas en charge. Et je pense que la réponse à cela est qu'il n'y avait pas de norme de flotteur non signée définie à l'origine. Puisque les langues aiment être rétrocompatibles, même si elles étaient ajoutées, les langues ne pourraient pas s'en servir. Pour voir la spécification en virgule flottante, vous devriez regarder la norme IEEE 754 Floating-Point .
Vous pouvez contourner le fait de ne pas avoir de type virgule flottante non signé en créant une classe float non signée qui encapsule un float ou un double et émet des avertissements si vous essayez de passer un nombre négatif. C'est moins efficace, mais probablement si vous ne les utilisez pas intensément, vous ne vous soucierez pas de cette légère perte de performance.
Je vois vraiment l'utilité d'avoir un flotteur non signé. Mais C / C ++ a tendance à choisir l'efficacité qui convient le mieux à tout le monde plutôt que la sécurité.
la source
int
's, toutes les vérifications de type liées aux signes peuvent avoir lieu au moment de la compilation. OP suggère queunsigned float
cela serait implémenté de manière régulièrefloat
avec des vérifications au moment de la compilation pour s'assurer que certaines opérations non significatives ne sont jamais effectuées. Le code machine et les performances qui en résultent peuvent être identiques, que vos flottants soient signés ou non.Il existe une différence significative entre les entiers signés et non signés en C / C ++:
les valeurs signées laissent le bit supérieur inchangé (extension de signe), les valeurs non signées effacent le bit supérieur.
La raison pour laquelle il n'y a pas de flottant non signé est que vous rencontrez rapidement toutes sortes de problèmes s'il n'y a pas de valeurs négatives. Considère ceci:
Quelle est la valeur de c? -8. Mais qu'est-ce que cela signifierait dans un système sans nombres négatifs. FLOAT_MAX - 8 peut-être? En fait, cela ne fonctionne pas car FLOAT_MAX - 8 est FLOAT_MAX en raison des effets de précision, donc les choses sont encore plus délicates. Et si cela faisait partie d'une expression plus complexe:
Ce n'est pas un problème pour les entiers en raison de la nature du système de complément à 2.
Considérez également les fonctions mathématiques standard: sin, cos et tan ne fonctionneraient que pour la moitié de leurs valeurs d'entrée, vous ne pouviez pas trouver le journal des valeurs <1, vous ne pouviez pas résoudre les équations quadratiques: x = (-b +/- racine ( bb - 4.ac)) / 2.a, et ainsi de suite. En fait, cela ne fonctionnerait probablement pas pour une fonction complexe car celles-ci ont tendance à être implémentées comme des approximations polynomiales qui utiliseraient des valeurs négatives quelque part.
Ainsi, les flotteurs non signés sont assez inutiles.
Mais cela ne veut pas dire qu'une classe dont la plage vérifie les valeurs flottantes n'est pas utile, vous pouvez vouloir limiter les valeurs à une plage donnée, par exemple les calculs RVB.
la source
2's complement
, il n'y aura pas de problème avec des flottants non signés?value >> shift for signed values leave the top bit unchanged (sign extend)
Êtes-vous sûr de cela? Je pensais que c'était un comportement défini par l'implémentation, du moins pour les valeurs signées négatives.0.0
, ou éventuellement Inf ou NaN. Ou soyez simplement un comportement indéfini, comme l'OP suggéré dans une modification de la question. Re: fonctions trigonométriques: ne définissez donc pas les versions d'entrée non signées desin
et ainsi de suite, et assurez-vous de traiter leur valeur de retour comme signée. La question ne proposait pas de remplacer float par float non signé, mais simplement en ajoutantunsigned float
un nouveau type.(En passant, Perl 6 vous permet d'écrire
et ensuite vous pouvez utiliser
Nonnegative::Float
comme vous le feriez pour tout autre type.)Il n'y a pas de support matériel pour les opérations en virgule flottante non signées, donc C ne le propose pas. C est principalement conçu pour être un «assemblage portable», c'est-à-dire aussi près du métal que possible sans être attaché à une plate-forme spécifique.
[Éditer]
C est comme l'assemblage: ce que vous voyez est exactement ce que vous obtenez. Un implicite "Je vais vérifier que ce flotteur n'est pas négatif pour vous" va à l'encontre de sa philosophie de conception. Si vous le voulez vraiment, vous pouvez ajouter
assert(x >= 0)
ou similaire, mais vous devez le faire explicitement.la source
of ...
pas.Je crois que l'int non signé a été créé en raison de la nécessité d'une marge de valeur plus grande que celle que l'int signataire pourrait offrir.
Un flotteur a une marge beaucoup plus grande, il n'y a donc jamais eu de besoin «physique» d'un flotteur non signé. Et comme vous le faites remarquer vous-même dans votre question, la précision supplémentaire de 1 bit n'est pas à tuer.
Edit: Après avoir lu la réponse de Brian R. Bondy , je dois modifier ma réponse: il a tout à fait raison de dire que les processeurs sous-jacents n'avaient pas d'opérations flottantes non signées. Cependant, je maintiens ma conviction qu'il s'agissait d'une décision de conception basée sur les raisons que j'ai énoncées ci-dessus ;-)
la source
Je pense que Treb est sur la bonne voie. Il est plus important pour les entiers que vous ayez un type correspondant non signé. Ce sont ceux qui sont utilisés dans le décalage de bits et utilisés dans les cartes de bits . Un petit signe vient juste sur le chemin. Par exemple, en décalant vers la droite une valeur négative, la valeur résultante est une implémentation définie en C ++. Faire cela avec un entier non signé ou déborder de celui-ci a une sémantique parfaitement définie car il n'y a pas de bit de ce type.
Donc, pour les entiers au moins, le besoin d'un type non signé séparé est plus fort que de simplement donner des avertissements. Tous les points ci-dessus ne doivent pas être pris en compte pour les flotteurs. Donc, je pense qu'il n'y a pas vraiment besoin de support matériel pour eux, et C ne les supportera déjà pas à ce stade.
la source
C99 prend en charge les nombres complexes et une forme générique de type sqrt, donc
sqrt( 1.0 * I)
sera négatif.Les commentateurs ont mis en évidence une légère brillance ci-dessus, en ce que je faisais référence à la
sqrt
macro de type générique plutôt qu'à la fonction, et elle renverra une valeur à virgule flottante scalaire par troncature du complexe à son composant réel:Il contient également un brain-pet, car la partie réelle du sqrt de tout nombre complexe est positive ou nulle, et sqrt (1.0 * I) est sqrt (0.5) + sqrt (0.5) * I not -1.0.
la source
sqrt(-0.0)
produit souvent-0.0
. Bien sûr, -0,0 n'est pas une valeur négative .Je suppose que cela dépend du fait que seules les spécifications à virgule flottante IEEE sont signées et que la plupart des langages de programmation les utilisent.
Article Wikipédia sur les nombres à virgule flottante IEEE-754
Edit: De plus, comme indiqué par d'autres, la plupart du matériel ne prend pas en charge les flottants non négatifs, de sorte que le type normal de flotteurs est plus efficace à faire car il existe un support matériel.
la source
Je pense que la raison principale est que les flotteurs non signés auraient des utilisations vraiment limitées par rapport aux ints non signés. Je n'accepte pas l'argument selon lequel c'est parce que le matériel ne le prend pas en charge. Les anciens processeurs n'avaient aucune capacité en virgule flottante, tout était émulé dans le logiciel. Si des flotteurs non signés étaient utiles, ils auraient d'abord été implémentés dans le logiciel et le matériel aurait emboîté le pas.
la source
Les types entiers non signés en C sont définis de manière à obéir aux règles d'un anneau algébrique abstrait. Par exemple, pour toute valeur X et Y, l'ajout de XY à Y donnera X. Les types entiers non signés sont garantis d'obéir à ces règles dans tous les cas qui n'impliquent pas de conversion vers ou à partir d'un autre type numérique [ou de types non signés de tailles différentes] , et cette garantie est l’une des caractéristiques les plus importantes de ces types. Dans certains cas, il vaut la peine de renoncer à la capacité de représenter des nombres négatifs en échange des garanties supplémentaires que seuls les types non signés peuvent offrir. Les types à virgule flottante, qu'ils soient signés ou non, ne peuvent pas respecter toutes les règles d'un anneau algébrique [par exemple, ils ne peuvent pas garantir que X + YY sera égal à X], et en effet IEEE ne le fait pas ' t leur permettre même de respecter les règles d'une classe d'équivalence [en exigeant que certaines valeurs se comparent inégales à elles-mêmes]. Je ne pense pas qu'un type à virgule flottante "non signé" puisse respecter les axiomes qu'un type à virgule flottante ordinaire ne pourrait pas, donc je ne suis pas sûr des avantages qu'il offrirait.
la source
IHMO c'est parce que la prise en charge des types à virgule flottante signés et non signés dans le matériel ou le logiciel serait trop gênant
Pour les types entiers, nous pouvons utiliser la même unité logique pour les opérations sur les entiers signés et non signés dans la plupart des situations en utilisant la belle propriété du complément de 2, car le résultat est identique dans ces cas pour les opérations add, sub, non-widening mul et la plupart des opérations au niveau du bit. Pour les opérations qui différencient la version signée et non signée, nous pouvons toujours partager la majorité de la logique . Par exemple
INT_MIN
. Également théoriquement possible, il n'est probablement pas utilisé sur le matériel, mais il est utile sur les systèmes qui ne prennent en charge qu'un seul type de comparaison (comme 8080 ou 8051)Les systèmes qui utilisent le complément de 1 ont également juste besoin d'une petite modification de la logique car c'est simplement le bit de report enroulé autour du bit le moins significatif. Je ne suis pas sûr des systèmes de magnitude des signes, mais il semble qu'ils utilisent le complément de 1 en interne, donc la même chose s'applique
Malheureusement, nous n'avons pas ce luxe pour les types à virgule flottante. En libérant simplement le bit de signe, nous aurons la version non signée. Mais alors à quoi devrions-nous utiliser ce bit?
Mais les deux choix nécessitent un additionneur plus grand pour s'adapter à la plage de valeurs plus large. Cela augmente la complexité de la logique alors que le bit supérieur de l'additionneur reste inutilisé la plupart du temps. Encore plus de circuits seront nécessaires pour les multiplications, les divisions ou d'autres opérations complexes
Sur les systèmes qui utilisent des logiciels à virgule flottante, vous avez besoin de 2 versions pour chaque fonction, ce qui n'était pas prévu pendant le temps où la mémoire était tellement chère, ou vous deviez trouver un moyen "délicat" de partager des parties des fonctions signées et non signées
Cependant, le matériel en virgule flottante existait bien avant l'invention de C , donc je pense que le choix en C était dû au manque de support matériel en raison de la raison que j'ai mentionnée ci-dessus
Cela dit, il existe plusieurs formats à virgule flottante non signés spécialisés , principalement à des fins de traitement d'image, comme le type à virgule flottante 10 et 11 bits du groupe Khronos.
la source
Je soupçonne que c'est parce que les processeurs sous-jacents ciblés par les compilateurs C n'ont pas un bon moyen de traiter les nombres à virgule flottante non signés.
la source
Bonne question.
Si, comme vous le dites, ce n'est que pour les avertissements au moment de la compilation et aucun changement dans leur comportement, sinon le matériel sous-jacent n'est pas affecté et en tant que tel, il ne s'agirait que d'un changement C ++ / Compiler.
J'ai gagné la même chose auparavant, mais le fait est que cela n'aiderait pas beaucoup. Au mieux, le compilateur peut trouver des affectations statiques.
Ou minimalistiquement plus
Mais c'est à peu près tout. Avec les types entiers non signés, vous obtenez également un wraparound défini, à savoir qu'il se comporte comme l'arithmétique modulaire.
après ce «uc» détient la valeur de 255.
Maintenant, que ferait un compilateur avec le même scénario avec un type float non signé? Si les valeurs ne sont pas connues au moment de la compilation, il faudrait générer du code qui exécute d'abord les calculs, puis effectue une vérification de signature. Mais que se passe-t-il lorsque le résultat d'un tel calcul serait de dire "-5,5" - quelle valeur doit être stockée dans un flottant déclaré non signé? On pourrait essayer l'arithmétique modulaire comme pour les types intégraux, mais cela vient avec ses propres problèmes: la plus grande valeur est incontestablement l'infini .... cela ne fonctionne pas, vous ne pouvez pas avoir "infinity - 1". Opter pour la plus grande valeur distincte qu'il peut contenir ne fonctionnera pas vraiment non plus, car vous y rencontrez une précision. "NaN" serait un candidat.
Enfin, ce ne serait pas un problème avec les nombres à virgule fixe car le modulo est bien défini.
la source