Integer summing blues, short += short problem

17,690

Solution 1

There are two questions here. The first is "why is short plus short result in int?"

Well, suppose short plus short was short and see what happens:

short[] prices = { 10000, 15000, 11000 };
short average = (prices[0] + prices[1] + prices[2]) / 3;

And the average is, of course, -9845 if this calculation is done in shorts. The sum is larger than the largest possible short, so it wraps around to negative, and then you divide the negative number.

In a world where integer arithmetic wraps around it is much more sensible to do all the calculations in int, a type which is likely to have enough range for typical calculations to not overflow.

The second question is:

  • short plus short is int
  • assigning int to short is illegal
  • a +=b is the same as a = a + b
  • therefore short += short should be illegal
  • so why is this legal?

The question has an incorrect premise; the third line above is wrong. The C# specification states in section 7.17.2

Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of x, and if y is implicitly convertible to the type of x or the operator is a shift operator, then the operation is evaluated as x = (T)(x op y), where T is the type of x, except that x is evaluated only once.

The compiler inserts the cast on your behalf. The correct reasoning is:

  • short plus short is int
  • assigning int to short is illegal
  • s1 += s2 is the same as s1 = (short)(s1 + s2)
  • therefore this should be legal

If it did not insert the cast for you then it would be impossible to use compound assignment on many types.

Solution 2

Well, the += operator says you'll be increasing the value of a with a short, while = says you'll overwrite the value, with the result of an operation. The operation a + b yields an int, not knowing that it can do otherwise, and you're trying to assign that int to a short.

Solution 3

You have to use:

a = (short)(a + b);

As to the difference between the behaviours of assignment and addition assignment, I imagine it has something to do with this (from msdn)

x+=y
is equivalent to
x = x + y
except that x is only evaluated once. The meaning of the + operator is
dependent on the types of x and y (addition for numeric operands, 
concatenation for string operands, and so forth).

However, it's a bit vague, so mabye someone with a deeper understanding can comment.

Solution 4

This happens because int is the smallest signed type for which + is defined. Anything smaller is first promoted to int. The += operator is defined with respect to +, but with a special-case rule for handling results that don't fit the target.

Solution 5

This is because += is implemented as an overloaded function (one of which is a short, and the compiler chooses the most specific overload). For the expression (a + b), the compiler widens the result to an int by default before assigning.

Share:
17,690
Mohammad Jahangeer Ansari
Author by

Mohammad Jahangeer Ansari

Software Engineer, Team Leader at MARG Software Solutions

Updated on June 23, 2022

Comments

  • Mohammad Jahangeer Ansari
    Mohammad Jahangeer Ansari about 2 years

    Program in C#:

    short a, b;
    a = 10;
    b = 10;
    a = a + b; // Error : Cannot implicitly convert type 'int' to 'short'.
    
    // we can also write this code by using Arithmetic Assignment Operator as given below
    
    a += b; // But this is running successfully, why?
    
    Console.Write(a);
    
  • Mohammad Jahangeer Ansari
    Mohammad Jahangeer Ansari over 13 years
    then what is the different between increase value vs add value ?
  • Camilo Martin
    Camilo Martin over 13 years
    I really didn't expect this. I thought += was just a shorthand!
  • Camilo Martin
    Camilo Martin over 13 years
    @jak I understand it as the = operator assigns a new value (in this case of the wrong type), but the += uses a different implementation (i.e., it adds as we expect it to).
  • Mohammad Jahangeer Ansari
    Mohammad Jahangeer Ansari over 13 years
    overloaded operator can only implemented with user defined operator like class , structure. but it is + an arithmetic operator, which is inbuilt.The += operator cannot be overloaded directly, but user-defined types can overload the + operator, for details visit link,,msdn.microsoft.com/en-us/library/sa7629ew(v=VS.71).asp‌​x
  • Øyvind Bråthen
    Øyvind Bråthen over 13 years
    I also always thought that a+=b was just a shorthand for a=a+b, so I would really like to see a confirmation here to be completely convinced.
  • UpTheCreek
    UpTheCreek over 13 years
    @jak: did you try it with both sets of parentheses? the code in your comment is different.
  • Camilo Martin
    Camilo Martin over 13 years
    @Øyvind Bråthen it seems to be like this: a+=b = a=a+b unless the += operator is overloaded, and it seems it is for type short, and there's no + for short, shortA + shortB widens and becomes (int)shortA + (int)shortB.
  • Øyvind Bråthen
    Øyvind Bråthen over 13 years
    @Camilo - So it seems, but I still want the confirmation to be sure ;)
  • Eric Lippert
    Eric Lippert over 13 years
    This answer is incorrect. += is not defined for each numeric type. See section 7.17.2 of the C# specification.
  • Eric Lippert
    Eric Lippert over 13 years
    @Camilo: there is no such thing as "overloading the += operator". Compound operators cannot be overloaded in C#.
  • Eric Lippert
    Eric Lippert over 13 years
    Where does it say that on MSDN? That is wrong if the + is a predefined operator, which in this case, clearly it is. In that case it is equivalent to x = (T)(x + y), as specified. If you can send me the link to the page, I'll bring it to the attention of the documentation managers.
  • Eric Lippert
    Eric Lippert over 13 years
    jak is correct. The += operator is not an overloaded function in C#.
  • Andrey
    Andrey over 13 years
    your first sample is not good. If you replace short with int everything will work fine, but if we put larger numbers (to make sum be greater than 2^31) it will cause int overflow. my question is why short + short = int (and for byte) and int + int = int (not long for example).
  • LBushkin
    LBushkin over 13 years
    Why was it decided that int's shouldn't widen to longs? I can understand the reasoning for widening the computation on bytes and shorts, but it seems just as likely that arithmetic on int could overflow as well. Is this just a concession to the fact that on 32bit architectures there are convenient instructions that maintain results in 32-bit registers? Or is there something deeper at play here?
  • Eric Lippert
    Eric Lippert over 13 years
    @LBushkin: Is it really "just as likely"? I write compilers for a living and I deal every day with compiling programs that have more than, say, 2^15 types. If I am adding together the number of types in a bunch of different assemblies I could easily overflow a short. I've never had a problem in the compiler space where I could reasonably overflow an int. The compiler would run out of virtual memory long before it managed to do arithmetic involving a program that has two billion types in it. It simply is not likely in most scenarios to have 32 bit overflow.
  • Eric Lippert
    Eric Lippert over 13 years
    @Andrey: Yes, integer arithmetic can overflow. If you want to avoid that then you can use the checked operator to ensure that integer overflows result in exceptions. If you feel that it is likely that you'll be working with integers large enough that common math operators on them can overflow then you should be doing all your arithmetic in longs in the first place.
  • Andrey
    Andrey over 13 years
    @Eric Lippert i was just wondering why the behaviour is different for short and int.
  • LBushkin
    LBushkin over 13 years
    @Eric: int is used in many computational contexts, some of which have no relation to memory/process space. As computing hardware increases in power & capacity, such boundaries are going to be surpassed fairly soon on a regular basis. But perhaps a better way of expressing my point is that if the compiler decides to widen smaller types to wider types when performing arithmetic to avoid unanticipated overflow, it should do so with all types that have an available wider type that preserves range and representational accuracy. The choice to stop at int (and not widen to long) is a bit unexpected.
  • Eric Lippert
    Eric Lippert over 13 years
    @Andrey: Because shorts and ints are very different. Most realistic calculations done in integers use only a tiny fraction of the range of an int; those same calculations use all or more than all of the range of a short. Most modern processors do all their integer math in 32 bits natively; you have to go way back in time to find processors that do work in 16 bits natively. I last worked on compilers for 16 bit Windows in 1997.
  • Eric Lippert
    Eric Lippert over 13 years
    @LBushkin: OK, so would you have us (1) say that int + int is long? Doing so is tantamount to saying that all calculations are always done in longs all the time. You don't want to make people insert casts in every integer calculation. (2) have C# have different behaviour depending on the architecture, as C does? (3) automatically widen to a larger type, as VBScript does; abandon static typing in C#. (4) something else? Each of these approaches has serious performance or portability costs. Are the costs worth it?
  • LBushkin
    LBushkin over 13 years
    @Eric: All of those are good points. To #1, C# is a language where all arithmetic is performed in ints all the time. Aside from the performance implications, I don't see a reason to prefer a narrower type to a wider one. Performance could very well be a deciding factor, however. To #2, I agree that different behavior on different architectures is undesirable and should be avoided. I don't think that we want to abandon static typing either. BTW, don't get me wrong; I'm just trying to understand the thinking behind the design decisions that go into creating a language like C#.
  • LBushkin
    LBushkin over 13 years
    @Eric: I guess I would argue that at times it would be nice to have finer grained control of when type widening takes place. I've often wanted to perform calculation on bytes or shorts that I did not want to have occur via promotion to int (since I was essentially performing bitwise computations). A syntax (perhaps similar to the checked/unchecked blocks) to allow that would be nice.
  • Eric Lippert
    Eric Lippert over 13 years
    @LBushkin: Why do you want to avoid such promotions? I assure you that both the CLR runtime layer and, ultimately, the CPU will widen those to int. Suppose for the sake of argument that we wanted to have short + short stay in shorts. The IL layer has no opcodes for that operation. It only has opcodes for integer addition. The CLR is going to convert those shorts to ints regardless. Suppose we added short addition operators to IL; now what is the jitter going to do? The chip is going to widen the operands to 32 bits when it enregisters them.
  • LBushkin
    LBushkin over 13 years
    @Eric: Typically, bytes and shorts are used to represent numbers - and so you want them to behave with the semantics of numbers. But sometimes you just need a ordered bag of bits - and you want to use arithmetic operations for their side-effects. The ^, |, &, <<, and >> bitwise operators retain the type of the expression they are applied to - but sometimes it can be useful to if +, -, *, and / could do so as well. Arithmetic with wraparound is occasionally useful. However, I understand your point about whether it's sufficiently valuable for enough people to design, develop, and test for C#.
  • LBushkin
    LBushkin over 13 years
    @Eric: Also, I was under the impression that at least on the x86 architecture, it's possible to perform move, load, and arithmetic on just the 16bit bit non-extended registers. And you could also access the low and high 8-bit subregisteres (eg. AL and AH within AX). Obviously, the behavior on non-x86 architectures could be substantively different.
  • UpTheCreek
    UpTheCreek over 13 years
    @Eric: Sure, it's on this page: msdn.microsoft.com/en-us/library/sa7629ew.aspx
  • Hadi Brais
    Hadi Brais over 2 years
    @LBushkin Re: "Why was it decided that int's shouldn't widen to longs?" The CLI was designed to run only on 32-bit and 64-bit architectures, not narrower architectures. That's why widening to 32 bits (like C/C++) but also making int to be always 32 bits in size (unlike C/C++) is a good design choice. So it's guaranteed that 32-bit arithmetic can be done efficiently on any target architecture. This is not true for 64-bit integer arithmetic on 32-bit architectures (long has a fixed 64-bit size in C#). In addition, on x86, instructions with 32-bit operands generally...
  • Hadi Brais
    Hadi Brais over 2 years
    ...have shorter lengths than with operands of other sizes. Promoting to int/uint strikes the best trade-off between range and portability of efficient arithmetic. Note that there is one case where 32-bit or narrower operands are promoted to long: if one operand is of type uint and the other is of type sbyte, short, char, or int, both are converted to long. Now if you do really want a narrow result (e.g. short+short=short), the impact of an explicit cast on the result of the widened operation on performance depends on whether the operation is in a checked or unchecked context.
  • Hadi Brais
    Hadi Brais over 2 years
    On x86, using .NET 6.0.1, the JIT compiler emits one unnecessary instruction for the expression unchecked((short)(a + b)) and four unnecessary instructions for the expression checked((short)(a + b)) compared to doing directly 16-bit addition. This can be significant in a loop. This is a missed optimization in the JIT because for these simple expressions, it's relatively easy to determine that the widened result is immediately truncated, so it can just emit 16-bit addition directly. This is what would happen if you compile an expression that is equiv to uncheck... in C++ using MSVC 19.30.