byte + byte = int… why?

2018-12-31 08:41发布

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.

16条回答
大哥的爱人
2楼-- · 2018-12-31 08:46

My suspicion is that C# is actually calling the operator+ defined on int (which returns an int unless you are in a checked block), and implicitly casting both of your bytes/shorts to ints. That's why the behavior appears inconsistent.

查看更多
其实,你不懂
3楼-- · 2018-12-31 08:47

From .NET Framework code:

// bytes
private static object AddByte(byte Left, byte Right)
{
    short num = (short) (Left + Right);
    if (num > 0xff)
    {
        return num;
    }
    return (byte) num;
}

// shorts (int16)
private static object AddInt16(short Left, short Right)
{
    int num = Left + Right;
    if ((num <= 0x7fff) && (num >= -32768))
    {
        return (short) num;
    }
    return num;
}

Simplify with .NET 3.5 and above:

public static class Extensions 
{
    public static byte Add(this byte a, byte b)
    {
        return (byte)(a + b);
    }
}

now you can do:

byte a = 1, b = 2, c;
c = a.Add(b);

查看更多
裙下三千臣
4楼-- · 2018-12-31 08:48

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 :(

查看更多
长期被迫恋爱
5楼-- · 2018-12-31 08:51

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:

  • If T1 is int and T2 is uint, or ulong, C1 is the better conversion.

Casting(C1) to int(T1) is better than casting(C2) to long(T2) because there is an implicit cast from int to long:

  • If an implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists, C1 is the better conversion.

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)

查看更多
何处买醉
6楼-- · 2018-12-31 08:52

This is because of overflow and carries.

If you add two 8 bit numbers, they might overflow into the 9th bit.

Example:

  1111 1111
+ 0000 0001
-----------
1 0000 0000

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.

查看更多
怪性笑人.
7楼-- · 2018-12-31 08:52

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

查看更多
登录 后发表回答