406 votes

octet + octet = int... pourquoi ?

En regardant ce code C# :

byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'

Le résultat de tout calcul effectué sur byte (ou short ) est implicitement retransformé en un nombre entier. La solution consiste à convertir explicitement le résultat en un octet :

byte z = (byte)(x + y); // this works

Ce que je me demande, c'est pourquoi ? Est-ce architectural ? Philosophique ?

Nous avons :

  • int + int = int
  • long + long = long
  • float + float = float
  • double + double = double

Alors pourquoi pas :

  • byte + byte = byte
  • short + short = short ?

Un peu de contexte : J'effectue une longue liste de calculs sur des "petits nombres" (c'est-à-dire < 8) et je stocke les résultats intermédiaires dans un grand tableau. En utilisant un tableau d'octets (au lieu d'un tableau d'int) est plus rapide (à cause des hits de cache). Mais les nombreux byte-casts répartis dans le code le rendent d'autant plus illisible.

6 votes

0 votes

Je suppose que c'est lié à la facilité avec laquelle on peut faire déborder un octet avec quelques ajouts. Cependant, je pense que c'est au programmeur de s'en occuper, plutôt qu'à l'architecte de le restaurer comme ça.

1 votes

Etes-vous sûr que vous n'êtes pas en train de micro-optimiser cela ? IIRC, sur une machine 32 bits, les octets sont alignés sur les frontières 32 bits pour un accès optimisé, c'est-à-dire qu'un octet utilise en réalité 4 octets en mémoire.

247voto

azheglov Points 3548

La troisième ligne de votre extrait de code :

byte z = x + y;

signifie en fait

byte z = (int) x + (int) y;

Ainsi, il n'y a pas d'opération + sur les octets, les octets sont d'abord convertis en entiers et le résultat de l'addition de deux entiers est un entier (32 bits).

0 votes

J'ai essayé le code ci-dessous mais il ne fonctionne toujours pas. byte z = (byte)x + (byte)y ;

11 votes

C'est parce qu'il n'y a pas d'opération + pour les octets (voir ci-dessus). Essayez byte z = (byte)( (int) x + (int) y)

40 votes

C'est sans doute la réponse la plus correcte et la plus concise. Il n'y a pas d'opérande à ajouter entre les octets, donc au lieu d'expliquer pourquoi "ajouter deux octets" fonctionne ou pas ( ça n'est jamais arrivé ), cela montre clairement pourquoi le résultat est un int, car la seule chose qui s'est produite est une addition de 2 ints .

185voto

Jon Skeet Points 692016

Pour ce qui est de la raison pour laquelle cela se produit, c'est parce qu'il n'y a pas d'opérateurs définis par C# pour l'arithmétique avec un octet, un sbyte, un short ou un ushort, comme d'autres l'ont dit. Cette réponse concerne pourquoi ces opérateurs ne sont pas définis.

Je crois que c'est essentiellement pour des raisons de performance. Les processeurs ont des opérations natives pour faire de l'arithmétique avec 32 bits très rapidement. La reconversion du résultat en octet se fait automatiquement. podría Il est possible de le faire, mais cela se traduirait par des pénalités de performance dans le cas où vous ne voulez pas réellement ce comportement.

I piense en Ce point est mentionné dans l'une des normes C# annotées. Vous cherchez...

EDIT : Malheureusement, j'ai parcouru la spécification ECMA C# 2 annotée, la spécification MS C# 3 annotée et la spécification CLI annotée, et je constate que aucun d'entre eux le mentionnent pour autant que je puisse voir. Je suis sûr J'ai vu la raison donnée ci-dessus, mais je suis soufflé si je sais où. Toutes mes excuses, fans de références :(

14 votes

Je suis désolé de dire ça, mais je trouve que ce n'est pas la meilleure réponse.

44 votes

As-tu descendu la carte chaque réponse que vous trouvez ne pas être la meilleure ? ;)

57 votes

(Pour clarifier, je ne m'en prends pas vraiment à vous. Il semble que chacun ait ses propres critères de déclassement, et c'est bien ainsi. Je ne downvote une réponse que si je pense qu'elle est activement nuisible plutôt que simplement non idéale).

70voto

Michael Petrotta Points 35647

I pensée J'avais déjà vu ça quelque part. De cet article, L'ancienne nouvelle chose :

Supposons que nous vivions dans un monde imaginaire où les opérations sur 'byte' donnent "octet".

byte b = 32;
byte c = 240;
int i = b + c; // what is i?

Dans ce monde imaginaire, la valeur de i serait de 16 ! Pourquoi ? Parce que les deux opérandes de l'opérateur + sont tous deux octets, donc la somme "b+c" est calculée en tant que un octet, ce qui donne 16 en raison d'un débordement des entiers. (Et, comme je l'ai noté plus tôt, le débordement d'entier est le nouveau vecteur d'attaque de sécurité).

EDITAR : Raymond défend, pour l'essentiel, l'approche adoptée à l'origine par le C et le C++. Dans les commentaires, il défend le fait que le C# adopte la même approche, au nom de la rétrocompatibilité du langage.

47 votes

Avec les entiers, si nous les additionnons et qu'ils débordent, ils ne sont pas automatiquement convertis en un autre type de données, alors pourquoi le faire avec les octets ?

0 votes

@Ryan : Je suppose que Microsoft prévoyait plus de problèmes avec l'arithmétique des octets qu'avec celle des int, en raison de la moindre dynamique des octets.

2 votes

Avec les ints, cela déborde. Essayez d'ajouter int.MaxValue + 1 et vous obtiendrez -2147483648 au lieu de 2147483648.

61voto

Alun Harford Points 2373

C#

L'ECMA-334 indique que l'addition est définie comme légale uniquement sur int+int, uint+uint, long+long et ulong+ulong (ECMA-334 14.7.4). En tant que telles, ces opérations sont les opérations candidates à prendre en compte dans le cadre de 14.4.2. Puisqu'il y a des casts implicites de byte à int, uint, long et ulong, tous les membres de la fonction d'addition sont des membres de fonction applicables sous 14.4.2.1. Nous devons trouver la meilleure conversion implicite selon les règles de 14.4.2.3 :

Le casting(C1) vers int(T1) est meilleur que le casting(C2) vers uint(T2) ou ulong(T2) car :

  • Si T1 est int et T2 est uint, ou ulong, C1 est la meilleure conversion.

Le transfert de (C1) vers int(T1) est préférable au transfert de (C2) vers long(T2) car il existe un transfert implicite de int vers long :

  • S'il existe une conversion implicite de T1 en T2, et qu'il n'existe pas de conversion implicite de T2 en T1, C1 est la meilleure conversion.

On utilise donc la fonction int+int, qui renvoie un int.

Ce qui revient à dire qu'elle est enfouie très profondément dans la spécification C#.

CLI

Le CLI ne fonctionne que sur 6 types (int32, int natif, int64, F, O, et &). (ECMA-335 partition 3 section 1.5)

Byte (int8) n'est pas un de ces types, et est automatiquement converti en int32 avant l'addition. (ECMA-335 partition 3 section 1.6)

0 votes

Le fait que l'ECMA ne spécifie que ces opérations particulières n'empêche pas un langage de mettre en œuvre d'autres règles. VB.NET permettra utilement byte3 = byte1 And byte2 sans cast, mais de manière peu utile, il lèvera une exception d'exécution si int1 = byte1 + byte2 donne une valeur supérieure à 255. Je ne sais pas si certains langages autorisent byte3 = byte1+byte2 et déclencher une exception lorsque ce chiffre dépasse 255, mais ne pas déclencher d'exception si int1 = byte1+byte2 donne une valeur comprise entre 256 et 510.

29voto

Christopher Points 5252

Les réponses indiquant une certaine inefficacité de l'addition d'octets et de la troncature du résultat en un octet sont incorrectes. Les processeurs x86 possèdent des instructions spécifiquement conçues pour les opérations sur les entiers sur des quantités de 8 bits.

En fait, pour les processeurs x86/64, l'exécution d'opérations sur 32 ou 16 bits est moins efficace que celle d'opérations sur 64 ou 8 bits en raison de l'octet de préfixe de l'opérande qui doit être décodé. Sur les machines 32 bits, l'exécution d'opérations 16 bits entraîne la même pénalité, mais il existe toujours des opcodes dédiés aux opérations 8 bits.

De nombreuses architectures RISC possèdent des instructions natives similaires, efficaces en termes de mots et d'octets. Celles qui n'en ont pas ont généralement une instruction de stockage et de conversion en valeur signée d'une certaine longueur de bit.

En d'autres termes, cette décision doit être fondée sur la perception de l'utilité du type d'octet, et non sur les inefficacités sous-jacentes du matériel.

0 votes

+1 ; si seulement cette perception n'était pas fausse à chaque fois que j'ai déplacé et mis en OU deux octets en C#...

0 votes

Il ne devrait pas y avoir de coût de performance pour tronquer le résultat. En assemblage x86, c'est juste la différence entre copier un octet du registre ou quatre octets du registre.

1 votes

@JonathanAllen Exactement. La seule différence est, assez ironiquement, que lors de l'exécution d'un élargissement conversion. Le site actuel L'exécution de l'instruction d'élargissement (qu'il s'agisse d'une extension signée ou d'une extension non signée) entraîne une pénalité de performance.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X