Looking at this C# code:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
The result of any math performed on byte
(or short
) types is implicitly cast back to an integer. The solution is to explicitly cast the result back to a byte:
byte z = (byte)(x + y); // this works
What I am wondering is why? Is it architectural? Philosophical?
We have:
int
+int
=int
long
+long
=long
float
+float
=float
double
+double
=double
So why not:
byte
+byte
=byte
short
+short
=short
?
A bit of background: I am performing a long list of calculations on "small numbers" (i.e. < 8) and storing the intermediate results in a large array. Using a byte array (instead of an int array) is faster (because of cache hits). But the extensive byte-casts spread through the code make it that much more unreadable.
My suspicion is that C# is actually calling the
operator+
defined onint
(which returns anint
unless you are in achecked
block), and implicitly casting both of yourbytes
/shorts
toints
. That's why the behavior appears inconsistent.From .NET Framework code:
Simplify with .NET 3.5 and above:
now you can do:
In terms of "why it happens at all" it's because there aren't any operators defined by C# for arithmetic with byte, sbyte, short or ushort, just as others have said. This answer is about why those operators aren't defined.
I believe it's basically for the sake of performance. Processors have native operations to do arithmetic with 32 bits very quickly. Doing the conversion back from the result to a byte automatically could be done, but would result in performance penalties in the case where you don't actually want that behaviour.
I think this is mentioned in one of the annotated C# standards. Looking...
EDIT: Annoyingly, I've now looked through the annotated ECMA C# 2 spec, the annotated MS C# 3 spec and the annotation CLI spec, and none of them mention this as far as I can see. I'm sure I've seen the reason given above, but I'm blowed if I know where. Apologies, reference fans :(
C#
ECMA-334 states that addition is only defined as legal on int+int, uint+uint, long+long and ulong+ulong (ECMA-334 14.7.4). As such, these are the candidate operations to be considered with respect to 14.4.2. Because there are implicit casts from byte to int, uint, long and ulong, all the addition function members are applicable function members under 14.4.2.1. We have to find the best implicit cast by the rules in 14.4.2.3:
Casting(C1) to int(T1) is better than casting(C2) to uint(T2) or ulong(T2) because:
Casting(C1) to int(T1) is better than casting(C2) to long(T2) because there is an implicit cast from int to long:
Hence the int+int function is used, which returns an int.
Which is all a very long way to say that it's buried very deep in the C# specification.
CLI
The CLI operates only on 6 types (int32, native int, int64, F, O, and &). (ECMA-335 partition 3 section 1.5)
Byte (int8) is not one of those types, and is automatically coerced to an int32 before the addition. (ECMA-335 partition 3 section 1.6)
This is because of overflow and carries.
If you add two 8 bit numbers, they might overflow into the 9th bit.
Example:
I don't know for sure, but I assume that
ints
,longs
, anddoubles
are given more space because they are pretty large as it is. Also, they are multiples of 4, which are more efficient for computers to handle, due to the width of the internal data bus being 4 bytes or 32 bits (64 bits is getting more prevalent now) wide. Byte and short are a little more inefficient, but they can save space.From the C# language spec 1.6.7.5 7.2.6.2 Binary numeric promotions it converts both operands to int if it can't fit it into several other categories. My guess is they didn't overload the + operator to take byte as a parameter but want it to act somewhat normally so they just use the int data type.
C# language Spec