tags:

views:

559

answers:

7

Why does the following raise a compile time error: 'Cannot implicitly convert type 'int' to 'byte':

        byte a = 25;
        byte b = 60;

        byte c = a ^ b;

This would make sense if I were using an arithmentic operator because the result of a + b could be larger than can be stored in a single byte.

However applying this to the XOR operator is pointless. XOR here it a bitwise operation that can never overflow a byte.

using a cast around both operands works:

byte c = (byte)(a ^ b);
+1  A: 

I guess its because the operator XOR is defined for booleans and integers.

And a cast of the result from the integer result to a byte is an information-losing conversion ; hence needs an explicit cast (nod from the programmer).

Gishu
A: 

This has more to do with the rules surrounding implicit and explicit casting in the CLI specification. An integer (int = System.Int32 = 4 bytes) is wider than a byte (1 byte, obviously!). Therefore any cast from int to byte is potentially a narrowing cast. Therefore, the compiler wants you to make this explicit.

James Webster
I think the surprise here for the poster is that the result isn't another byte.
Lasse V. Karlsen
+1  A: 

The demigod programmer from Microsoft has an answer: http://blogs.msdn.com/oldnewthing/archive/2004/03/10/87247.aspx

And maybe it's more about compiler design. They make the compiler simpler by generalizing the compiling process, it doesn't have to look at operator of operands, so it lumped bitwise operations in the same category as arithmetic operators. Thereby, subjected to type widening

Michael Buen
I've just read that. He was actually talking about arithmetic operators not bitwise. He agrees in the comments that his point is not valid for bitwise operators.
Ash
@Ash: nice for asking him. maybe Anders should amend the compiler and make it more intelligent, and afaict C doesn't have that behavior
Michael Buen
@Michael: That's because C, in general, doesn't require explicit casting between different sizes of integers.
dan04
A: 

I thought I remembered a popular question about this.

http://stackoverflow.com/questions/941584/byte-byte-int-why

Spencer Ruport
It's not quite the same thing. Adding two bytes can overflow. XORing two bytes cannot.
dan04
A: 

It seems to be because in C# language specifications, it is defined for integer and long http://msdn.microsoft.com/en-us/library/aa691307%28v=VS.71%29.aspx

So, what actually happens is that compiler casts byte operands to int implicitly because there is no loss of data that way. But the result (which is int) can not be down-cast-ed without loss of data (implicitly). So, you need to tell the compiler explicitly that you know what you are doing!

Omer Akhter
+5  A: 

I can't give you the rationale, but I can tell why the compiler has that behavior from the stand point of the rules the compiler has to follow (which might not really be what you're interesting in knowing).

From an old copy of the C# spec (I should probably download a newer version), emphasis added:

14.2.6.2 Binary numeric promotions This clause is informative.

Binary numeric promotion occurs for the operands of the predefined +, ?, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here:

  • If either operand is of type decimal, the other operand is converted to type decimal, or a compile-time error occurs if the other operand is of type float or double.
  • Otherwise, if either operand is of type double, the other operand is converted to type double.
  • Otherwise, if either operand is of type float, the other operand is converted to type float.
  • Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or a compile-time error occurs if the other operand is of type sbyte, short, int, or long.
  • Otherwise, if either operand is of type long, the other operand is converted to type long.
  • Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
  • Otherwise, if either operand is of type uint, the other operand is converted to type uint.
  • Otherwise, both operands are converted to type int.

So, basically operands smaller than an int will be converted to int for these operators (and the result will be an int for the non-relational ops).

I said that I couldn't give you a rationale; however, I will make a guess at one - I think that the designers of C# wanted to make sure that operations that might lose information if narrowed would need to have that narrowing operation made explicit by the programmer in the form of a cast. For example:

byte a = 200;
byte b = 100;

byte c = a + b;  // value would be truncated

While this kind of truncation wouldn't happen when performing an xor operation between two byte operands, I think that the language designers probably didn't want to have a more complex set of rules where some operations would need explicit casts and other not.


Just a small note: the above quote is 'informational' not 'normative', but it covers all the cases in an easy to read form. Strictly speaking (in a normative sense), the reason the ^ operator behaves this way is because the closest overload for that operator when dealing with byte operands is (from 14.10.1 "Integer logical operators"):

int operator ^(int x, int y); 

Therefore, as the informative text explains, the operands are promoted to int and an int result is produced.

Michael Burr
+1, Nice answer, Raymond Chen said pretty much the same, Microsoft believe consistency between types is important for ease of usage.
Ash
A: 

As to why the two bytes have to be converted to ints to do the XOR?

If you want to dig into it, 12.1.2 of the CLI Spec (Partition I) describes the fact that, on the evaluation stack, only int or long can exist. All shorter integral types have to be expanded during evaluation.

Unfortunately, I can't find a suitable link directly to the CLI Spec - I've got a local copy as PDF, but can't remember where I got it from.

Damien_The_Unbeliever