web-dev-qa-db-fra.com

Pourquoi C n'a-t-il pas de flotteurs non signés?

Je sais, la question semble être étrange. Les programmeurs pensent parfois trop. S'il vous plaît lire sur ...

En C, j'utilise beaucoup signed et unsigned nombres entiers. 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 chars? Une racine carrée ne retournera certainement jamais un nombre négatif. Il existe également d'autres endroits où une valeur flottante négative n'a aucune signification. Candidat idéal pour un char non signé.

Btw - Je ne suis pas vraiment intéressé par la précision supplémentaire que je pourrais obtenir en supprimant le bit de signalisation des flotteurs. Je suis super content de floats tels qu'ils sont en ce moment. Je voudrais juste marquer un float comme non signé parfois et obtenir le même genre d'avertissements que ceux 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 pourquoi ils n'existent pas?


MODIFIER:

Je sais que la FPU x87 n’a aucune instruction pour traiter les flottants non signés. Permet simplement d’utiliser les instructions float signées. Une mauvaise utilisation (par exemple, aller au-dessous de zéro) pourrait être considérée comme un comportement non défini de la même manière qu'un débordement d'entiers signés n'est pas défini.

115
Nils Pipenbrinck

Pourquoi C++ ne prend pas en charge les flottants non signés, c'est qu'il n'y a pas d'opération de code machine équivalente à exécuter par la CPU. Il serait donc très inefficace de le soutenir.

Si C++ le supportait, vous utiliseriez parfois un float non signé et ne réalisiez pas que votre performance venait juste de disparaître. Si C++ le prend en charge, chaque opération en virgule flottante doit être vérifiée pour déterminer si elle est signée ou non. Et pour les programmes qui effectuent des millions d'opérations en virgule flottante, cela n'est pas acceptable.

La question serait donc de savoir pourquoi les implémenteurs matériels ne la prennent pas en charge. Et je pense que la réponse à cette question est qu’il n’y avait pas de norme float 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 l'utiliser. Pour voir la spécification de virgule flottante, vous devez regarder virgule flottante standard IEEE 754 .

Vous pouvez éviter le type de virgule flottante non signé en créant une classe float non signée qui encapsule un nombre flottant ou double et émet des avertissements si vous essayez de passer un nombre négatif. Ceci 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 performances.

Je vois vraiment l'utilité d'avoir un char non signé. Mais C/C++ a tendance à choisir l'efficacité qui convient le mieux à la sécurité de tous.

103
Brian R. Bondy

Il existe une différence significative entre les entiers signés et non signés en C/C++:

value >> shift

les valeurs signées ne modifient pas le bit du haut (extension du signe), les valeurs non signées effacent le bit du haut.

La raison pour laquelle il n'y a pas de float 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:

float a = 2.0f, b = 10.0f, c;
c = a - b;

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, ce qui rend les choses encore plus difficiles. Et si cela faisait partie d'une expression plus complexe:

float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
e = (a - b) / d + c;

Ce n'est pas un problème pour les entiers en raison de la nature du système du 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 du second degré: x = (-b +/- racine bb - 4.ac))/2.a, etc. En fait, cela ne fonctionnerait probablement pas pour une fonction complexe, car celles-ci ont tendance à être implémentées sous forme d'approximations polynomiales utilisant des valeurs négatives quelque part.

Ainsi, les flotteurs non signés sont plutôt inutiles.

Mais cela ne signifie pas pour autant qu'une classe dont la plage vérifie les valeurs flottantes ne soit pas utile; vous souhaiterez peut-être limiter les valeurs à une plage donnée, par exemple les calculs RVB.

13
Skizz

(En aparté, Perl 6 vous permet d’écrire

subset Nonnegative::Float of Float where { $_ >= 0 };

et ensuite vous pouvez utiliser Nonnegative::Float comme vous le feriez avec tout autre type.)

Il n'y a pas de support matériel pour les opérations en virgule flottante non signée, donc C ne l'offre pas. C est principalement conçu pour être un "assemblage portable", c’est-à-dire aussi près que possible du métal sans être attaché à une plate-forme spécifique.

[modifier]

C est comme une assemblée: ce que vous voyez est exactement ce que vous obtenez. Un commentaire implicite "Je vérifierai que ce flotteur est non 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.

8
ephemient

Je pense que le unsigned int a été créé pour répondre à la nécessité de disposer d’une marge de valeur supérieure à celle que l’entier signé pourrait offrir.

Un flottant a une marge beaucoup plus grande, il n'y a donc jamais eu de besoin "physique" pour un flottant non signé. Et comme vous le dites vous-même dans votre question, la précision supplémentaire de 1 bit n’a rien de grave.

Edit: Après avoir lu le réponse de Brian R. Bondy , je dois modifier ma réponse: il a tout à fait raison de dire que le Les processeurs sous-jacents n'avaient pas d'opération float non signée. Cependant, je persiste à croire qu'il s'agissait d'une décision de conception fondée sur les raisons que j'ai exposées ci-dessus ;-)

7
Treb

Je pense que Treb est sur la bonne voie. Pour les entiers, il est plus important que vous ayez un type correspondant non signé. Ce sont ceux qui sont utilisés dans décalage de bits et utilisés dans bit-maps. Un peu de signe entre juste dans le chemin. Par exemple, en décalant à 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 ce type a une sémantique parfaitement définie car aucun bit de ce type ne gêne.

Ainsi, pour les entiers au moins, la nécessité d'un type non signé distinct est plus importante que de simplement donner des avertissements. Tous les points ci-dessus n'ont pas besoin d'être pris en compte pour les flotteurs. Donc, je pense qu’il n’ya aucun besoin réel de support matériel pour eux, et C ne les supportera déjà plus à ce stade.

6

Je suppose que cela dépend du fait que seules les spécifications de points flottants IEEE sont signées et que la plupart des langages de programmation les utilisent.

article sur les nombres à virgule flottante

Edit: De plus, comme d’autres l'ont fait remarquer, la plupart des matériels ne prennent pas en charge les flottants non négatifs. Le type de flotteurs normal est donc plus efficace car il prend en charge le matériel.

5
Tobias Wärre

Une racine carrée ne retournera jamais un nombre négatif. Il existe également d'autres endroits où une valeur flottante négative n'a aucune signification. Candidat idéal pour un char non signé.

C99 supporte les nombres complexes et une forme générique de type sqrt, donc sqrt( 1.0 * I) sera négatif.


Les commentateurs ont souligné un léger éclat ci-dessus, dans la mesure où je faisais référence à la macro générique type sqrt plutôt qu'à la fonction. Cette dernière renverrait une valeur en virgule flottante scalaire en tronquant le complexe en son composant réel:

#include <complex.h>
#include <tgmath.h>

int main () 
{
    complex double a = 1.0 + 1.0 * I;

    double f = sqrt(a);

    return 0;
}

Il contient également un cerveau-péter, comme la partie réelle de la sqrt de tout nombre complexe est positif ou zéro, et sqrt (1.0 * I) est sqrt (0.5) + sqrt (0.5) * I pas -1.0.

4
Pete Kirkham

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 processeurs plus anciens n’avaient aucune capacité en virgule flottante, tout était imité par un logiciel. Si les flottants non signés étaient utiles, ils auraient d'abord été implémentés dans un logiciel, suivi du matériel.

3
Ferruccio

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. Il est garanti que les types entiers non signés obéissent à ces règles dans tous les cas ne nécessitant 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 ce type. Dans certains cas, il vaut la peine d'abandonner la possibilité de représenter des nombres négatifs en échange des garanties supplémentaires que seuls les types non signés peuvent fournir. 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 ex. ils ne peuvent pas garantir que X + Y-Y sera égal à X], et effectivement, l'IEEE ne leur permet même pas de respecter les règles d'une classe d'équivalence [en exigeant que certaines valeurs se comparent de manière inégale par rapport à elles-mêmes]. Je ne pense pas qu'un type à virgule flottante "non signé" puisse respecter un axiome qu'un mot ordinaire à virgule flottante ne pourrait pas, alors je ne suis pas sûr des avantages qu'il offrirait.

2
supercat

Je suppose que c'est parce que les processeurs sous-jacents ciblés par les compilateurs C ne disposent pas d'un bon moyen de gérer les nombres à virgule flottante non signés.

1
Brian Ensink

IHMO, c’est parce qu’il serait trop compliqué de prendre en charge les types à virgule flottante signés et non signés, qu’il s’agisse de logiciels ou de matériels.

Pour les types entiers, nous pouvons utiliser la même unité logique pour les opérations entières signées et non signées = dans la plupart des situations, en utilisant la propriété Nice du complément à 2, car le résultat est identique dans les cas pour les opérations add, sub, non-widening et la plupart des bits. Pour les opérations qui différencient les versions signée et non signée, nous pouvons toujours partager la majorité de la logique. Par exemple

  • L'arithmétique et le décalage logique ne nécessitent qu'un léger changement dans la charge pour les bits supérieurs
  • L'élargissement de la multiplication peut utiliser le même matériel pour la partie principale, puis une logique distincte pour ajuster le résultat permet de changer la signature . Non pas qu'il soit utilisé dans de vrais multiplicateurs, mais il est possible de le faire
  • La comparaison signée peut être convertie facilement en comparaison non signée et vice versa en basculant le bit le plus haut ou en ajoutant INT_MIN . Aussi théoriquement possible, il n’est probablement pas utilisé sur le matériel, mais il est utile sur les systèmes ne prenant en charge qu’un seul type de comparaison (comme 8080 ou 8051)

Les systèmes qui utilisent le complément à 1 ont également besoin d'une petite modification de la logique, car il s'agit simplement du bit de retenue enroulé au bit le moins significatif. Pas sûr des systèmes à amplitude de signe, mais il semble qu'ils tilise 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, pourquoi devrions-nous utiliser ce bit?

  • Augmentez la plage en l'ajoutant à l'exposant
  • Augmentez la précision en l'ajoutant à la mantisse. Ceci est souvent plus utile, car nous avons généralement besoin de plus de précision que la portée.

Mais les deux choix nécessitent un plus grand additionneur pour s'adapter à la plage de valeurs la plus large. Cela accroît la complexité de la logique pendant que le bit le plus haut de l'additionneur reste inutilisé la plupart du temps. Encore plus de circuits seront nécessaires pour les multiplications, divisions ou autres opérations complexes

Sur les systèmes utilisant des logiciels à virgule flottante, vous avez besoin de 2 versions pour chaque fonction, ce qui n'était pas prévu pendant la période où la mémoire coûtait cher, ou vous deviez trouver un moyen "astucieux" de partager des parties des fonctions signées et non signées

Cependant le matériel en virgule flottante existait bien avant que C ait été inventé , je pense donc que le choix en C était dû au manque de support matériel à cause de la raison que j'ai mentionnée ci-dessus

Cela dit, il existe plusieurs spécialistes formats à virgule flottante non signée, principalement utilisés à des fins de traitement d'images, comme Type à virgule flottante 10 et 11 bits du groupe Khronos

0
phuclv