2010-01-15 2 views
1

Je suis un programmeur Java essayant de migrer vers C#, et ce Gotcha m'a déconcerté légèrement:Bitwise - ou semble avoir une distribution implicite à long terme. Cela peut-il être évité?

int a = 1; 

a = 0x08000000 | a; 
a = 0x80000000 | a; 

La première ligne compile très bien. La seconde ne le fait pas. Il semble reconnaître qu'il existe une constante avec un bit de signe, et pour une raison quelconque, il décide de jeter le résultat d'une longue, entraînant l'erreur:

Cannot implicitly convert type 'long' to 'int'.
An explicit conversion exists (are you missing a cast?)

La solution que j'ai à ce jour est:

a = (int)(0x80000000 | a); 

qui traite de la fonte, mais laisse encore un avertissement:

Bitwise-or operator used on a sign-extended operand;
consider casting to a smaller unsigned type first

Quelle serait la bonne façon de C# pour exprimer une erreur/avertissement/moyen à long libre?

Répondre

12

Un entier numérique est un int littéral par défaut, à moins que le nombre est trop grand pour tenir dans une int et il devient un uint à la place (et ainsi de suite pour long et ulong). Comme la valeur 0x80000000 est trop grande pour entrer dans un int, il s'agit d'une valeur uint. Lorsque vous utilisez l'opérateur | sur un int et un tous les deux sont étendus à long car aucun ne peut être converti en toute sécurité à l'autre.

La valeur peut être représentée par un int, mais vous devez ignorer le fait qu'elle devient une valeur négative. Le compilateur ne le fera pas en silence, donc vous devez le charger de faire la valeur d'un int sans se soucier du trop-plein:

a = unchecked((int)0x80000000) | a; 

(Note: Ceci indique que le compilateur comment convertir la valeur, donc il est pas de code créé pour faire la conversion en int.)

+0

Non, c'est 'uint': http://msdn.microsoft.com/en-us/library/aa664674%28VS.71%29.aspx – Joey

+0

En fait, cela ressemble à la meilleure solution. Merci. – izb

+0

Et pour le code-tidyness parce que j'ai beaucoup de masques: privé const int MASK_80000000 = non coché ((int) 0x80000000); – izb

1

Votre problème est dû au fait que 0x80000000 est un sous-formulaire int et que vous ne pouvez pas effectuer d'opérations au niveau du bit sur les valeurs négatives.

Cela devrait fonctionner correctement si vous utilisez un uint.

a = ((uint)0x80000000) | a; //assuming a is a uint 
+0

'' 0x80000000' est déjà uint', selon le cahier des charges: http://msdn.microsoft.com/en-us/library/aa664674%28VS.71%29.aspx – Joey

+0

Cela ne fait aucune différence J'ai peur. – izb

+6

Cette explication est fausse. Vous pouvez effectuer des opérations au niveau du bit sur les entiers négatifs. –

1

Changer cette ligne à
(int)((uint)0x80000000 | (uint)a);
fait pour moi.

+0

Cela semble faire l'affaire :) Quelqu'un sait-il si ces moulages ont un coût d'exécution? Ou le compilateur range-t-il tout intelligemment? – izb

+2

Le système d'exécution sous-jacent ne fait pas de distinction entre int et uint; c'est juste un seau de 32 bits. Les casts seront réduits à néant. –

0

le problème que vous avez ici est que

  • 0x80000000 est un entier non signé littéral. La spécification indique qu'un entier littéral est du premier type dans la liste (int, uint, long, ulong) qui peut contenir le littéral. Dans ce cas, il s'agit de uint.
  • a est probablement un int

Cela provoque le résultat être un long. Je ne vois pas de meilleur moyen que de renvoyer le résultat à int, sauf si vous savez que a ne peut pas être négatif. Ensuite, vous pouvez lancer a à uint ou déclarer de cette façon en premier lieu.

12

Je trouve intéressant que dans toutes ces réponses, une seule personne ait effectivement suggéré de faire ce que l'avertissement dit. L'avertissement vous indique comment résoudre le problème. faites attention à cela. Le bit ou l'opérateur est utilisé sur un opérande d'extension de signe: int. Cela provoque la conversion du résultat en un type plus important: long. Un type non signé plus petit que long est uint. Alors faites ce que l'avertissement dit; lancer l'opérande prolongé par un signe - int - to uint:

result = (int)(0x80000000 | (uint) operand); 

Il n'y a plus d'extension de signe.

Bien sûr, cela soulève la question la plus large: pourquoi traiter un entier signé comme un champ de bit en premier lieu? Cela semble être une chose dangereuse à faire.

0

Vous pouvez le rendre moins laid en créant une constante qui commence par la lettre «O». (Et contrairement à ce site, en studio visuel, il ne l'affiche pas dans une couleur différente).

const int Ox80000000 = unchecked((int)0x80000000); 

/// ... 

const int every /* */ = Ox80000000; 
const int thing /* */ = 0x40000000; 
const int lines /* */ = 0x20000000; 
const int up /*  */ = 0x10000000; 
Questions connexes