Je ne peux pas comprendre pourquoi les systèmes à microprocesseur implémentent des nombres non signés. Je suppose que le coût est juste le double du nombre de branches conditionnelles, car supérieur à, inférieur à, .etc, a besoin d'un algorithme différent de signé, existe-t-il encore des algorithmes pour lesquels les nombres non signés sont un avantage significatif?
ma question est en partie pourquoi ils doivent être dans le jeu d'instructions plutôt que d'être pris en charge par un compilateur?
Réponses:
Les nombres non signés sont une interprétation d'une séquence de bits. C'est également l'interprétation la plus simple et la plus utilisée en interne au CPU car les adresses et les codes op ne sont que des bits. L'adressage mémoire / pile et l'arithmétique sont les fondements du microprocesseur, enfin, du traitement. En remontant la pyramide d'abstraction, une autre interprétation fréquente des bits est celle d'un caractère (ASCII, Unicode, EBCDIC). Ensuite, il existe d'autres interprétations telles que IEEE Floating virgule, RGBA pour les graphiques, etc. Aucun de ceux-ci n'est de simples nombres signés (IEEE FP n'est pas simple, et l'arithmétique qui les utilise est très compliquée).
De plus, avec l'arithmétique non signée, il est assez simple (sinon le plus efficace) d'implémenter les autres. L'inverse est pas vrai.
la source
La majeure partie du coût du matériel pour les opérations de comparaison est la soustraction. La sortie de la soustraction utilisée par comparaison est essentiellement trois bits d'état:
Avec la bonne combinaison de test de ces trois bits après l'opération de soustraction, nous pouvons déterminer toutes les opérations relationnelles signées, ainsi que toutes les opérations relationnelles non signées (ces bits sont également la façon dont le dépassement est détecté, signé vs non signé). Le même matériel ALU de base peut être partagé pour implémenter toutes ces comparaisons (sans parler de l'instruction de soustraction), jusqu'à la vérification finale de ces trois bits d'état, qui diffère selon la comparaison relationnelle souhaitée. Donc, ce n'est pas beaucoup de matériel supplémentaire.
Le seul coût réel est la nécessité du codage de modes de comparaison supplémentaires dans l'architecture du jeu d'instructions, ce qui peut diminuer légèrement la densité des instructions. Pourtant, il est assez normal que le matériel ait beaucoup d'instructions qui ne sont pas utilisées par une langue donnée.
la source
Parce que, si vous avez besoin de compter quelque chose qui est toujours
>= 0
, vous réduisez inutilement votre espace de comptage de moitié en utilisant des entiers signés.Considérez le PK INT auto-incrémenté que vous pourriez mettre sur vos tables de base de données. Si vous y utilisez un entier signé, votre table stocke la MOITIÉ autant d'enregistrements que possible pour la même taille de champ sans aucun avantage.
Ou les octets d'une couleur RGBa. Nous ne voulons pas commencer maladroitement à compter ce concept de nombre naturellement positif à un nombre négatif. Un numéro signé briserait le modèle mental ou diviserait de moitié notre espace. Un entier non signé correspond non seulement au concept, mais fournit deux fois la résolution.
Du point de vue matériel, les entiers non signés sont simples. Ils sont probablement la structure de bits la plus simple pour effectuer des calculs. Et, sans aucun doute, nous pourrions simplifier le matériel en simulant des types entiers (ou même virgule flottante!) Dans un compilateur. Alors, pourquoi les entiers non signés et signés sont-ils implémentés dans le matériel?
Et bien ... la performance!
Il est plus efficace d'implémenter des entiers signés dans le matériel que dans le logiciel. Le matériel peut être chargé d'effectuer des calculs sur l'un ou l'autre type d'entier dans une seule instruction. Et c'est très bien , car le matériel écrase les bits plus ou moins en parallèle. Si vous essayez de simuler cela dans un logiciel, le type entier que vous choisissez de "simuler" nécessitera sans aucun doute de nombreuses instructions et sera sensiblement plus lent.
la source
Votre question se compose de deux parties:
Quel est le but des entiers non signés?
Les entiers non signés valent-ils la peine?
1. Quel est le but des entiers non signés?
Les nombres non signés représentent tout simplement une classe de quantités pour lesquelles les valeurs négatives n'ont pas de sens. Bien sûr, vous pourriez dire que la réponse à la question "combien de pommes ai-je?" peut être un nombre négatif si vous devez des pommes à quelqu'un, mais qu'en est-il de la question de "combien de mémoire ai-je?" --vous ne pouvez pas avoir une quantité de mémoire négative. Ainsi, les entiers non signés sont très appropriés pour représenter de telles quantités, et ils ont l'avantage de pouvoir représenter deux fois la plage de valeurs positives que les entiers signés peuvent. Par exemple, la valeur maximale que vous pouvez représenter avec un entier signé 16 bits est 32767, tandis qu'avec un entier non signé 16 bits, elle est 65535.
2. Les entiers non signés valent-ils la peine?
Les entiers non signés ne représentent pas vraiment de problème, alors, oui, ils en valent la peine. Vous voyez, ils ne nécessitent pas un ensemble supplémentaire d '"algorithmes"; les circuits requis pour les implémenter sont un sous-ensemble des circuits requis pour implémenter des entiers signés.
Un processeur n'a pas un multiplicateur pour les entiers signés et un multiplicateur différent pour les entiers non signés; il n'a qu'un seul multiplicateur, qui fonctionne de manière légèrement différente selon la nature de l'opération. La prise en charge de la multiplication signée nécessite un tout petit peu plus de circuits que non signé, mais comme elle doit être prise en charge de toute façon, la multiplication non signée est pratiquement gratuite, elle est incluse dans le package.
En ce qui concerne l'addition et la soustraction, il n'y a aucune différence dans les circuits. Si vous lisez la représentation dite des compléments à deux des entiers, vous constaterez qu'elle est si intelligemment conçue que ces opérations peuvent être effectuées exactement de la même manière, quelle que soit la nature des entiers.
La comparaison fonctionne également de la même manière, car il ne s'agit que de soustraire et d'éliminer le résultat, la seule différence réside dans les instructions de branchement conditionnel (saut), qui fonctionnent en examinant différents drapeaux du processeur définis par le instruction (de comparaison) précédente. Dans cette réponse: /programming//a/9617990/773113, vous pouvez trouver une explication de leur fonctionnement sur l'architecture Intel x86. Ce qui se passe, c'est que la désignation d'une instruction de saut conditionnel comme signée ou non signée dépend des indicateurs qu'elle examine.
la source
Les microprocesseurs sont intrinsèquement non signés. Les numéros signés sont la chose qui est mise en œuvre, et non l'inverse.
Les ordinateurs peuvent fonctionner et fonctionnent bien sans numéros signés, mais c'est nous, les humains qui ont besoin de nombres négatifs, d'où la signature a été inventée.
la source
Parce qu'ils ont un bit de plus qui est facilement disponible pour le stockage, et vous n'avez pas à vous soucier des nombres négatifs. Il n'y a pas grand-chose de plus que cela.
Maintenant, si vous avez besoin d'un exemple de l'endroit où vous auriez besoin de ce bit supplémentaire, il y a beaucoup à trouver si vous regardez.
Mon exemple préféré vient des bitboards dans les moteurs d'échecs. Il y a 64 cases sur un échiquier,
unsigned long
offrant ainsi un stockage parfait pour une variété d'algorithmes tournant autour de la génération de mouvements. Compte tenu du fait que vous avez besoin d'opérations binaires (ainsi que d'opérations de décalage !!), il est facile de voir pourquoi il est plus facile de ne pas avoir à se soucier de ce qui se passe si le MSB est défini. Cela peut être fait avec signé long, mais il est beaucoup plus facile d'utiliser non signé.la source
Ayant une formation en mathématiques pures, c'est une approche légèrement plus mathématique pour quiconque s'intéresse.
Si nous commençons avec un entier signé et non signé 8 bits, ce que nous avons est essentiellement les entiers modulo 256, en ce qui concerne l'addition et la multiplication, à condition que le complément de 2 soit utilisé pour représenter des entiers négatifs (et c'est ainsi que tous les processeurs modernes le font) .
Là où les choses diffèrent, c'est à deux endroits: l'un est les opérations de comparaison. Dans un sens, les entiers modulo 256 sont mieux considérés comme un cercle de nombres (comme le font les entiers modulo 12 sur une horloge analogique à l'ancienne). Pour que les comparaisons numériques (x <y) soient significatives, nous devions décider quels nombres sont inférieurs aux autres. Du point de vue du mathématicien, nous voulons incorporer les entiers modulo 256 dans l'ensemble de tous les entiers d'une manière ou d'une autre. Le mappage de l'entier 8 bits dont la représentation binaire est tous des zéros à l'entier 0 est la chose évidente à faire. Nous pouvons ensuite procéder à la cartographie des autres afin que '0 + 1' (le résultat de la mise à zéro d'un registre, disons ax, et de l'incrémentation de un, via 'inc ax') passe à l'entier 1, et ainsi de suite. Nous pouvons faire de même avec -1, par exemple en mappant «0-1» à l'entier -1 et «0-1-1» à l'entier -2. Nous devons nous assurer que cette intégration est une fonction, donc nous ne pouvons pas mapper un seul entier 8 bits à deux entiers. En tant que tel, cela signifie que si nous mappons tous les nombres dans l'ensemble d'entiers, 0 sera là, ainsi que certains entiers inférieurs à 0 et certains supérieurs à 0. Il existe essentiellement 255 façons de le faire avec un entier 8 bits (selon au minimum que vous voulez, de 0 à -255). Ensuite, vous pouvez définir «x <y» en termes de «0 <y - x».
Il existe deux cas d'utilisation courants, pour lesquels la prise en charge matérielle est judicieuse: l'un avec tous les entiers non nuls supérieurs à 0, et l'autre avec une répartition d'environ 50/50 autour de 0. Toutes les autres possibilités sont facilement émulées en traduisant les nombres via un ajout supplémentaire. et sub 'avant les opérations, et la nécessité de cela est si rare que je ne peux pas penser à un exemple explicite dans un logiciel moderne (puisque vous pouvez simplement travailler avec une mantisse plus grande, disons 16 bits).
L'autre problème est celui de la mise en correspondance d'un entier 8 bits dans l'espace des nombres entiers 16 bits. -1 va-t-il à -1? C'est ce que vous voulez si 0xFF est censé représenter -1. Dans ce cas, l'extension du signe est la chose la plus judicieuse à faire, de sorte que 0xFF passe à 0xFFFF. D'un autre côté, si 0xFF était censé représenter 255, alors vous voulez qu'il soit mappé à 255, donc à 0x00FF, plutôt qu'à 0xFFFF.
C'est également la différence entre les opérations de «décalage» et de «décalage arithmétique».
En fin de compte, cependant, cela revient au fait que les int dans le logiciel ne sont pas des entiers, mais des représentations en binaire, et que seuls certains peuvent être représentés. Lors de la conception du matériel, des choix doivent être faits quant à ce qu'il faut faire nativement dans le matériel. Comme avec le complément à 2, les opérations d'addition et de multiplication sont identiques, il est logique de représenter les entiers négatifs de cette façon. Il ne s'agit alors que d'opérations qui dépendent des entiers que vos représentations binaires sont censées représenter.
la source
Permet d'examiner le coût de mise en œuvre pour ajouter des entiers non signés à une conception de processeur avec des entiers signés existants.
Un CPU typique a besoin des instructions arithmétiques suivantes:
Il a également besoin d'instructions logiques:
Pour effectuer les branches ci-dessus sur des comparaisons entières signées, la manière la plus simple est de faire en sorte que l'instruction SUB définisse les indicateurs suivants:
Ensuite, les branches arithmétiques sont implémentées comme suit:
Les négations de celles-ci devraient découler évidemment de la façon dont elles sont mises en œuvre.
Ainsi, votre conception existante implémente déjà tout cela pour les entiers signés. Voyons maintenant ce que nous devons faire pour ajouter des entiers non signés:
Notez que dans chaque cas, les modifications sont très simples et peuvent être implémentées simplement en activant ou désactivant une petite section de circuits, ou en ajoutant un nouveau registre d'indicateur qui peut être contrôlé par une valeur qui doit être calculée dans le cadre de la mise en œuvre de l'instruction de toute façon.
Par conséquent, le coût de l'ajout d'instructions non signées est très faible . Quant à savoir pourquoi cela doit être fait , notez que les adresses mémoire (et les décalages dans les tableaux) sont des valeurs intrinsèquement non signées. Comme les programmes passent beaucoup de temps à manipuler les adresses mémoire, le fait d'avoir un type qui les gère correctement facilite l'écriture des programmes.
la source
Les nombres non signés existent en grande partie pour gérer les situations où l'on a besoin d'un anneau algébrique enveloppant (pour un type non signé 16 bits, ce serait l'anneau d'entiers congruents mod 65536). Prenez une valeur, ajoutez une quantité inférieure au module, et la différence entre les deux valeurs sera la quantité qui a été ajoutée. Par exemple, si un compteur d'utilité lit 9995 au début d'un mois et que l'on utilise 23 unités, le compteur affichera 0018 à la fin du mois. Lorsque vous utilisez un type d'anneau algébrique, il n'est pas nécessaire de faire quoi que ce soit de spécial pour faire face au débordement. La soustraction de 9995 à 0018 donnera 0023, précisément le nombre d'unités qui ont été utilisées.
Sur le PDP-11, la machine pour laquelle C a été implémenté pour la première fois, il n'y avait pas de types entiers non signés, mais des types signés pouvaient être utilisés pour l'arithmétique modulaire qui était comprise entre 32767 et -32768 plutôt qu'entre 65535 et 0. Les instructions entières sur d'autres cependant, les plates-formes n’ont pas fait le tour plutôt que d'exiger que les implémentations doivent émuler les deux entiers complémentaires utilisés dans le PDP-11, le langage a plutôt ajouté des types non signés qui devaient principalement se comporter comme des anneaux algébriques, et a permis aux types entiers signés de se comporter d'autres manières en cas de débordement.
Dans les premiers jours de C, il y avait de nombreuses quantités qui pouvaient dépasser 32767 (le commun INT_MAX) mais pas 65535 (le commun UINT_MAX). Il est donc devenu courant d'utiliser des types non signés pour contenir de telles quantités (par exemple size_t). Malheureusement, rien dans le langage ne fait la distinction entre les types qui devraient se comporter comme des nombres avec un peu de plage positive, et les types qui devraient se comporter comme des anneaux algébriques. Au lieu de cela, le langage fait que les types plus petits que "int" se comportent comme des nombres tandis que les types de taille normale se comportent comme des anneaux algébriques. Par conséquent, la fonction d'appel comme:
avec (65535, 65535) aura un comportement défini sur les systèmes où
int
est 16 bits (c'est-à-dire retour 1), un comportement différent oùint
est 33 bits ou plus (retour 0xFFFE0001), et un comportement indéfini sur les systèmes où "int" est n'importe où dans entre [notez que gcc donnera généralement des résultats arithmétiquement corrects avec des résultats entre INT_MAX + 1u et UINT_MAX, mais générera parfois du code pour la fonction ci-dessus qui échoue avec de telles valeurs!]. Pas très utile.Pourtant, le manque de types qui se comportent toujours comme des nombres ou comme un anneau algébrique ne change pas le fait que les types d'anneaux algébriques sont presque indispensables pour certains types de programmation.
la source