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
) les types est implicitement restitué en entier. La solution consiste à convertir explicitement le résultat en octet:
byte z = (byte)(x + y); // this works
Je me demande pourquoi? Est-ce architectural? Philosophique?
On a:
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. L'utilisation d'un tableau d'octets (au lieu d'un tableau int) est plus rapide (en raison des accès au cache). Mais les octets étendus diffusés dans le code le rendent encore plus illisible.
c#
type-conversion
Robert Cartaino
la source
la source
byte1 | byte2
ne les traite pas du tout comme des nombres. Cela les traite précisément comme des modèles de bits. Je comprends votre point de vue, mais il se trouve que chaque fois que je faisais de l'arithmétique sur des octets en C #, je les traitais en fait comme des bits, pas des nombres, et ce comportement est toujours gênant.Réponses:
La troisième ligne de votre extrait de code:
signifie en fait
Il n'y a donc 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).
la source
int
: stackoverflow.com/a/43578929/4561887En termes de "pourquoi cela se produit", c'est parce qu'il n'y a aucun opérateur défini par C # pour l'arithmétique avec octet, sbyte, court ou ushort, comme d'autres l'ont dit. Cette réponse explique 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 conversion automatique du résultat en octet pourrait être effectuée, mais entraînerait des pénalités de performances dans le cas où vous ne souhaitez pas réellement ce comportement.
Je pense que cela est mentionné dans l'une des normes annotées C #. Vous cherchez ...
EDIT: De manière ennuyeuse, j'ai maintenant parcouru la spécification ECMA C # 2 annotée, la spécification MS C # 3 annotée et la spécification CLI d'annotation, et aucune d'entre elles ne mentionne cela pour autant que je puisse voir. Je suis sûr d' avoir vu la raison donnée ci-dessus, mais je suis stupéfait si je sais où. Excuses, fans de référence :(
la source
Je pensais avoir déjà vu ça quelque part. De cet article, The Old New Thing :
EDIT : Raymond défend essentiellement l'approche C et C ++ adoptée à l'origine. Dans les commentaires, il défend le fait que C # adopte la même approche, pour des raisons de compatibilité descendante du langage.
la source
C #
L'ECMA-334 déclare que l'addition est uniquement définie comme légale sur int + int, uint + uint, long + long et ulong + ulong (ECMA-334 14.7.4). En tant que telles, ce sont les opérations candidates à considérer en ce qui concerne 14.4.2. Parce qu'il y a des transtypages implicites d'octet en entier, uint, long et ulong, tous les membres de fonction d'addition sont des membres de fonction applicables sous 14.4.2.1. Nous devons trouver la meilleure distribution implicite par les règles du 14.4.2.3:
La conversion (C1) en int (T1) est meilleure que la conversion (C2) en uint (T2) ou ulong (T2) car:
Le cast (C1) en int (T1) est meilleur que le cast (C2) en long (T2) car il y a un cast implicite de int en long:
Par conséquent, la fonction int + int est utilisée, qui renvoie un int.
Ce qui est très long pour dire qu'il est enfoui très profondément dans la spécification C #.
CLI
La CLI fonctionne uniquement sur 6 types (int32, int natif, int64, F, O et &). (ECMA-335 partition 3 section 1.5)
L'octet (int8) ne fait pas partie de ces types et est automatiquement contraint à un int32 avant l'ajout. (ECMA-335 partition 3 section 1.6)
la source
byte3 = byte1 And byte2
sans transtypage, mais lèvera inutilement une exception d'exécution siint1 = byte1 + byte2
renvoie une valeur supérieure à 255. Je ne sais pas si des langages autoriseraientbyte3 = byte1+byte2
et lèveraient une exception lorsque cela dépasse 255, mais ne lèveraient pas d'exception si lesint1 = byte1+byte2
rendements une valeur comprise entre 256 et 510.Les réponses indiquant une inefficacité en ajoutant des octets et en tronquant le résultat en un octet sont incorrectes. Les processeurs x86 ont des instructions spécialement conçues pour un fonctionnement entier sur des quantités de 8 bits.
En fait, pour les processeurs x86 / 64, l'exécution d'opérations 32 bits ou 16 bits est moins efficace que les opérations 64 bits ou 8 bits en raison de l'octet de préfixe d'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 pour les opérations 8 bits.
De nombreuses architectures RISC ont des instructions natives efficaces par mot / octet. Ceux qui n'ont généralement pas de valeur de stockage et de conversion en valeur signée d'une longueur de bit.
En d'autres termes, cette décision doit avoir été basée sur la perception de la destination du type d'octet, et non en raison de l'inefficacité sous-jacente du matériel.
la source
byte
(etchar
), mais pas pourshort
lequel sémantiquement est clairement un nombre.Je me souviens d'avoir lu quelque chose de Jon Skeet (je ne le trouve pas maintenant, je continuerai à chercher) sur la façon dont l'octet ne surcharge pas réellement l'opérateur +. En fait, lors de l'ajout de deux octets comme dans votre exemple, chaque octet est en fait implicitement converti en entier. Le résultat est évidemment un int. Maintenant, pourQUOI cela a été conçu de cette façon, j'attendrai que Jon Skeet lui-même poste :)
EDIT: Je l'ai trouvé! Excellentes informations sur ce même sujet ici .
la source
C'est à cause du débordement et des portages.
Si vous ajoutez deux nombres à 8 bits, ils peuvent déborder dans le 9ème bit.
Exemple:
Je ne sais pas avec certitude, mais je suppose que
ints
,longs
etdoubles
on leur donne plus d'espace car ils sont assez grands comme ça. En outre, ce sont des multiples de 4, qui sont plus efficaces à gérer pour les ordinateurs, car la largeur du bus de données interne est de 4 octets ou 32 bits (64 bits devient de plus en plus répandu maintenant). Octet et court sont un peu plus inefficaces, mais ils peuvent économiser de l'espace.la source
A partir de la spécification de langage C # 1.6.7.5 7.2.6.2 Promotions numériques binaires, il convertit les deux opérandes en int s'il ne peut pas les intégrer dans plusieurs autres catégories. Je suppose qu'ils n'ont pas surchargé l'opérateur + pour prendre l'octet comme paramètre, mais veulent qu'il agisse quelque peu normalement, donc ils utilisent simplement le type de données int.
Spécification du langage C #
la source
Je soupçonne que C # appelle en fait le
operator+
défini surint
(qui renvoie unint
sauf si vous êtes dans unchecked
bloc), et transforme implicitement vos deuxbytes
/shorts
enints
. C'est pourquoi le comportement semble incohérent.la source
C'était probablement une décision pratique de la part des concepteurs de langage. Après tout, un int est un Int32, un entier signé 32 bits. Chaque fois que vous effectuez une opération entière sur un type plus petit que int, il sera de toute façon converti en 32 bits signé int par la plupart des CPU 32 bits. Cela, combiné à la probabilité de débordement de petits entiers, a probablement scellé l'affaire. Cela vous évite la corvée de vérifier en permanence le débordement / sous-débit, et lorsque le résultat final d'une expression sur les octets serait à portée, malgré le fait qu'à un stade intermédiaire, il serait hors de portée, vous obtenez un correct résultat.
Une autre pensée: le débordement / sous-débit sur ces types devrait être simulé, car il ne se produirait pas naturellement sur les CPU cibles les plus probables. Pourquoi s'embêter?
la source
C'est en grande partie ma réponse qui se rapporte à ce sujet, soumise d'abord à une question similaire ici .
Toutes les opérations dont les nombres entiers sont inférieurs à Int32 sont arrondies à 32 bits avant le calcul par défaut. La raison pour laquelle le résultat est Int32 est simplement de le laisser tel quel après le calcul. Si vous vérifiez les opcodes arithmétiques MSIL, le seul type numérique intégral avec lequel ils fonctionnent est Int32 et Int64. C'est "par conception".
Si vous souhaitez que le résultat revienne au format Int16, cela n'a pas d'importance si vous effectuez le transtypage en code, ou si le compilateur (hypotétiquement) émet la conversion "sous le capot".
Par exemple, pour faire de l'arithmétique Int16:
Les deux nombres s'élargiraient à 32 bits, seraient ajoutés, puis tronqués à 16 bits, c'est ainsi que MS le voulait.
L'avantage de l'utilisation courte (ou octet) est principalement le stockage dans les cas où vous avez des quantités massives de données (données graphiques, streaming, etc.)
la source
L'addition n'est pas définie pour les octets. Ils sont donc convertis en int pour l'ajout. Cela est vrai pour la plupart des opérations et octets mathématiques. (notez que c'est comme ça dans les langues plus anciennes, je suppose que cela reste vrai aujourd'hui).
la source
Je pense que c'est une décision de conception sur laquelle l'opération était la plus courante ... Si octet + octet = octet, peut-être que beaucoup plus de gens seront gênés par le transtypage en int lorsqu'un int est requis.
la source
À partir du code .NET Framework:
Simplifiez avec .NET 3.5 et supérieur:
maintenant vous pouvez faire:
la source
J'ai testé les performances entre octet et int.
Avec des valeurs int:
Avec des valeurs d'octets:
Voici le résultat:
octet: 3,57s 157mo, 3,71s 171mo, 3,74s 168mo avec CPU ~ = 30%
int: 4,05s 298mo, 3,92s 278mo, 4,28 294mo avec CPU ~ = 27%
Conclusion: les
octets utilisent plus le CPU mais il coûte moins de mémoire et c'est plus rapide (peut-être parce qu'il y a moins d'octets à allouer)
la source
En plus de tous les autres excellents commentaires, j'ai pensé que j'ajouterais une petite friandise. Beaucoup de commentaires se sont demandé pourquoi int, long et à peu près tout autre type numérique ne suit pas également cette règle ... retourne un type "plus gros" en réponse à l'arithmatique.
Beaucoup de réponses ont trait à la performance (enfin, 32 bits est plus rapide que 8 bits). En réalité, un nombre 8 bits est toujours un nombre 32 bits pour un processeur 32 bits .... même si vous ajoutez deux octets, le bloc de données sur lequel le processeur opère sera de 32 bits malgré tout ... donc l'ajout d'ints ne va pas être "plus rapide" que d'ajouter deux octets ... c'est tout de même la CPU. MAINTENANT, l'ajout de deux pouces sera plus rapide que l'ajout de deux longs sur un processeur 32 bits, car l'ajout de deux longs nécessite plus de microops car vous travaillez avec des nombres plus larges que le mot du processeur.
Je pense que la raison fondamentale pour laquelle l'arithmétique des octets se traduit par des nombres entiers est assez claire et simple: 8 bits ne va tout simplement pas très loin! : D Avec 8 bits, vous avez une plage non signée de 0 à 255. Ce n'est pas beaucoup de place pour travailler avec ... la probabilité que vous allez rencontrer des limitations d'octets est TRÈS élevée lorsque vous les utilisez en arithmétique. Cependant, la chance que vous allez manquer de bits lorsque vous travaillez avec des pouces, ou des longs, ou des doubles, etc. est considérablement plus faible ... suffisamment faible pour que nous rencontrions très rarement le besoin de plus.
La conversion automatique d'octet en entier est logique car l'échelle d'un octet est si petite. La conversion automatique de int en long, float en double, etc. n'est pas logique car ces nombres ont une échelle significative.
la source
byte - byte
retoursint
, ou pourquoi ils ne font pas de casting pourshort
...byte + byte
renvoieint
, car 255 + tout ce qui est supérieur à ce qu'un octet peut contenir, cela n'a pas de sens qu'un octet moins un autre octet renvoie autre chose qu'un entier du point de vue de la cohérence du type de retour.byte
soustraction retournerait abyte
, et l'addition d'octets retournerait ashort
(byte
+byte
s'inscrira toujours dans ashort
). S'il s'agissait de cohérence comme vous le dites,short
cela suffirait toujours pour les deux opérations plutôt queint
. De toute évidence, il y a un mélange de raisons, pas nécessairement toutes bien pensées. Ou, la raison des performances donnée ci-dessous peut être plus précise.