Les processeurs Intel (et peut-être certains autres) utilisent le petit format endian pour le stockage.
Je me demande toujours pourquoi quelqu'un voudrait stocker les octets dans l'ordre inverse. Ce format présente-t-il des avantages par rapport au format big endian?
Il y a des arguments de toute façon, mais un point est que dans un système petit-boutiste, l'adresse d'une valeur donnée en mémoire, prise comme une largeur de 32, 16 ou 8 bits, est la même.
En d'autres termes, si vous avez en mémoire une valeur à deux octets:
0x00f0 16
0x00f1 0
prendre ce "16" comme une valeur de 16 bits (c "court" sur la plupart des systèmes 32 bits) ou comme une valeur de 8 bits (généralement c "char") ne change que l'instruction de récupération que vous utilisez - pas l'adresse que vous récupérez de.
Sur un système big-endian, avec ce qui précède présenté comme:
0x00f0 0
0x00f1 16
vous devez incrémenter le pointeur, puis effectuer l'opération d'extraction plus étroite sur la nouvelle valeur.
Donc, en bref, "sur les petits systèmes endiens, les transtypages sont un no-op."
Je me demande toujours pourquoi quelqu'un voudrait stocker les octets dans l'ordre inverse.
Big-endian et little-endian ne sont que "l'ordre normal" et "l'ordre inverse" d'un point de vue humain, et seulement si tous ces éléments sont vrais ...
Ce sont toutes des conventions humaines qui n'ont aucune importance pour un processeur. Si vous deviez conserver # 1 et # 2, et inverser # 3, le petit-endian semblerait "parfaitement naturel" aux personnes qui lisent l'arabe ou l'hébreu, qui s'écrivent de droite à gauche.
Et il y a d'autres conventions humaines qui font que le big-endian ne semble pas naturel, comme ...
À l'époque où je programmais principalement 68K et PowerPC, je considérais le big-endian comme étant "correct" et le petit-endian comme étant "incorrect". Mais depuis que je fais plus ARM et Intel travaillent, je me suis habitué au petit-boutien. Cela n'a vraiment pas d'importance.
OK, voici la raison telle que je l'ai expliquée: Addition et soustraction
Lorsque vous ajoutez ou soustrayez des nombres multi-octets, vous devez commencer par l'octet le moins significatif. Si vous ajoutez deux nombres de 16 bits par exemple, il peut y avoir un report de l'octet le moins significatif vers l'octet le plus significatif, vous devez donc commencer par l'octet le moins significatif pour voir s'il y a un report. C'est la même raison que vous commencez par le chiffre le plus à droite lorsque vous effectuez l'ajout à la main. Vous ne pouvez pas commencer par la gauche.
Prenons un système 8 bits qui récupère les octets séquentiellement dans la mémoire. S'il récupère d'abord l'octet le moins significatif , il peut commencer à faire l'ajout tandis que l'octet le plus significatif est extrait de la mémoire. Ce parallélisme est la raison pour laquelle les performances sont meilleures en petit endian sur un système par exemple. S'il devait attendre que les deux octets soient récupérés de la mémoire, ou les récupérer dans l'ordre inverse, cela prendrait plus de temps.
C'est sur les anciens systèmes 8 bits. Sur un processeur moderne, je doute que l'ordre des octets fasse une différence et nous n'utilisons le petit endian que pour des raisons historiques.
Avec les processeurs 8 bits, c'était certainement plus efficace, vous pouviez effectuer une opération 8 ou 16 bits sans avoir besoin de code différent et sans avoir besoin de tamponner des valeurs supplémentaires.
C'est encore mieux pour certaines opérations d'addition si vous traitez un octet à la fois.
Mais il n'y a aucune raison pour que le big-endian soit plus naturel - en anglais, vous utilisez treize (petit endian) et vingt-trois (big endian)
La convention de date japonaise est "big endian" - aaaa/mm/jj. C'est pratique pour les algorithmes de tri, qui peuvent utiliser une simple chaîne de comparaison avec la règle habituelle du premier caractère qui est la plus significative.
Quelque chose de similaire s'applique aux nombres big-endian stockés dans un enregistrement de champ le plus significatif en premier. L'ordre de signification des octets dans les champs correspond à la signification des champs dans l'enregistrement, vous pouvez donc utiliser un memcmp
pour comparer les enregistrements, peu importe si vous comparez deux mots longs, quatre mots ou huit octets séparés.
Inversez l'ordre de signification des champs et vous obtenez le même avantage, mais pour les nombres peu endiens plutôt que les gros-boutiens.
Cela n'a bien sûr que très peu d'importance pratique. Que votre plate-forme soit big-endian ou little-endian, vous pouvez commander des champs d'enregistrements pour exploiter cette astuce si vous en avez vraiment besoin. C'est juste une douleur si vous avez besoin d'écrire du code portable.
Je pourrais aussi bien inclure un lien vers l'appel classique ...
http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt
[~ # ~] modifier [~ # ~]
Une pensée supplémentaire. J'ai écrit une fois une grande bibliothèque d'entiers (pour voir si je pouvais), et pour cela, les morceaux de 32 bits de large sont stockés dans un ordre peu endian, quelle que soit la façon dont la plate-forme ordonne les bits dans ces morceaux. Les raisons étaient ...
De nombreux algorithmes commencent naturellement à fonctionner à l'extrémité la moins significative et souhaitent que ces extrémités soient mises en correspondance. Par exemple, en plus, le porte propage à des chiffres de plus en plus significatifs, il est donc logique de commencer à la fin la moins significative.
Augmenter ou réduire une valeur signifie simplement ajouter/supprimer des morceaux à la fin - pas besoin de déplacer les morceaux vers le haut/bas. La copie peut encore être nécessaire en raison d'une réallocation de mémoire, mais pas souvent.
Cela n'a pas de pertinence évidente pour les processeurs, bien sûr - jusqu'à ce que les CPU soient fabriqués avec un support matériel à gros nombres entiers, c'est purement une chose de bibliothèque.
Personne d'autre n'a répondu POURQUOI cela pourrait être fait, beaucoup de choses sur les conséquences.
Considérons un processeur 8 bits qui peut charger un seul octet de la mémoire dans un cycle d'horloge donné.
Maintenant, si vous voulez charger une valeur de 16 bits, dans (disons) le seul et unique registre de 16 bits que vous avez - c'est-à-dire le compteur de programme, alors un moyen simple de le faire est:
le résultat: vous n'incrémentez que l'emplacement de récupération, vous ne chargez que dans la partie inférieure de votre registre plus large, et vous avez seulement besoin de pouvoir déplacer vers la gauche. (Bien sûr, le décalage vers la droite est utile pour d'autres opérations, donc celui-ci est un peu un spectacle secondaire.)
Une conséquence de cela est que le contenu de 16 bits (double octet) est stocké dans l'ordre Most..Least. C'est-à-dire que la plus petite adresse a l'octet le plus significatif - donc gros endian.
Si vous avez plutôt essayé de charger en utilisant peu d'endian, vous devrez charger un octet dans la partie inférieure de votre registre large, puis charger l'octet suivant dans une zone de transit, le déplacer, puis le placer en haut de votre registre plus large . Ou utilisez une disposition plus complexe de portes pour pouvoir charger sélectivement dans l'octet supérieur ou inférieur.
Le résultat d'essayer d'aller peu endian est que vous avez besoin de plus de silicium (commutateurs et portes), ou plus d'opérations.
En d'autres termes, en termes de rapport qualité/prix dans le passé, vous en avez plus pour la plupart des performances et la plus petite surface de silicium.
De nos jours, ces considérations et à peu près hors de propos, mais des choses comme le remplissage du pipeline peut sont toujours un gros problème.
Quand il s'agit d'écrire s/w, la vie est souvent plus facile lorsque l'on utilise un petit adressage endian.
(Et les processeurs big endian ont tendance à être big endian en termes de classement des octets et little endian en termes de bits en octets. Mais certains processeurs sont étranges et utiliseront le classement des bits big endian ainsi que le classement des octets. très intéressant pour le concepteur h/w ajoutant des périphériques mappés en mémoire mais n'a aucune autre conséquence pour le programmeur.)
jimwise a fait un bon point. Il y a un autre problème, en petit endian vous pouvez faire ce qui suit:
byte data[4];
int num=0;
for(i=0;i<4;i++)
num += data[i]<<i*8;
OR
num = *(int*)&data; //is interpreted as
mov dword data, num ;or something similar it has been some time
Plus simple pour les programmeurs qui ne sont pas affectés par l'inconvénient évident des emplacements échangés dans la mémoire. Personnellement, je trouve que le gros endian est inverse de ce qui est naturel :). 12 doit être stocké et écrit comme 21 :)
Je me demande toujours pourquoi quelqu'un voudrait stocker les octets dans l'ordre inverse
Les nombres décimaux sont écrits en gros endian. C'est aussi comment vous l'écrivez en anglais Vous commencez avec le chiffre le plus significatif et le suivant le plus significatif au moins le plus significatif. par exemple.
1234
est mille deux cent trente-quatre.
C'est ainsi que le gros endian est parfois appelé l'ordre naturel.
En petit endian, ce nombre serait un, vingt, trois cent quatre mille.
Cependant, lorsque vous effectuez une arithmétique comme l'addition ou la soustraction, vous commencez par la fin.
1234
+ 0567
====
Vous commencez par 4 et 7, écrivez le chiffre le plus bas et souvenez-vous du report. Ensuite, vous ajoutez 3 et 6, etc. Pour ajouter, soustraire ou comparer, il est plus simple à mettre en œuvre, si vous avez déjà une logique pour lire la mémoire dans l'ordre, si les nombres sont inversés.
Pour prendre en charge le big endian de cette façon, vous avez besoin d'une logique pour lire la mémoire à l'envers, ou vous avez un processus RISC qui ne fonctionne que sur des registres. ;)
Une grande partie de la conception Intel x86/AMD x64 est historique.
Le big-endian est utile pour certaines opérations (comparaisons de "bignums" de ressorts de longueur d'octet égale à l'esprit). Little-endian pour les autres (en ajoutant éventuellement deux "bignums"). En fin de compte, cela dépend de la configuration du matériel du processeur, c'est généralement l'un ou l'autre (certaines puces MIPS étaient, IIRC, commutables au démarrage pour être LE ou BE).
Lorsque seuls le stockage et le transfert avec des longueurs variables sont impliqués, mais pas d'arithmétique avec plusieurs valeurs, alors LE est généralement plus facile à écrire, tandis que BE est plus facile à lire.
Prenons une conversion int-to-string (et retour) comme exemple spécifique.
int val_int = 841;
char val_str[] = "841";
Lorsque l'int est converti en chaîne, le chiffre le moins significatif est plus facile à extraire que le chiffre le plus significatif. Tout cela peut être fait dans une boucle simple avec une condition de fin simple.
val_int = 841;
// Make sure that val_str is large enough.
i = 0;
do // Write at least one digit to care for val_int == 0
{
// Constants, can be optimized by compiler.
val_str[i] = '0' + val_int % 10;
val_int /= 10;
i++;
}
while (val_int != 0);
val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it
Essayez maintenant la même chose dans l'ordre BE. Habituellement, vous avez besoin d'un autre diviseur qui détient la plus grande puissance de 10 pour le nombre spécifique (ici 100). Vous devez d'abord trouver cela, bien sûr. Beaucoup plus de choses à faire.
La conversion de chaîne en entier est plus facile à faire dans BE, lorsqu'elle est effectuée en tant qu'opération d'écriture inverse. Write enregistre le dernier chiffre le plus significatif, il doit donc être lu en premier.
val_int = 0;
length = strlen(val_str);
for (i = 0; i < length; i++)
{
// Again a simple constant that can be optimized.
val_int = 10*val_int + (val_str[i] - '0');
}
Faites de même dans l'ordre LE. Encore une fois, vous auriez besoin d'un facteur supplémentaire commençant par 1 et multiplié par 10 pour chaque chiffre.
Ainsi, je préfère généralement utiliser BE pour le stockage, car une valeur est écrite exactement une fois, mais lue au moins une fois et peut-être plusieurs fois. Pour sa structure plus simple, j'utilise généralement la route pour convertir en LE, puis inverser le résultat, même s'il écrit la valeur une deuxième fois.
Un autre exemple pour le stockage BE serait le codage UTF-8, et bien d'autres.