tags:

views:

328

answers:

4

I have the following example, compiled in VS2005, warning level 4:

int main(int argc, char *argv[])
{
    short s = 2;
    short t = 3;

    t *= s;    // warning C4244: '*=' : conversion from 'int' to 'short', possible loss of data
    t = t * s;
}

It doesn't seem to me there should be a warning on either line.

Does *= create an implicit conversion to int for some reason?

EDIT:

Seems like the lack of warning on the second line (and in VS2008) are the real questions.

Thanks for the answers.

+3  A: 

I believe the answer is here.

Quote:

Most C operators perform type conversions to bring the operands of an expression to a common type or to extend short values to the integer size used in machine operations.

McAden
+4  A: 

The result of a short * short could overflow a short, so it's probably storing the intermediate result in an int in the second example. If anything they both should have warnings.

The first one is probably complaining because it's storing the result directly in the original short value, while the second one is creating an intermediate int and then casting that to a short (still a lossy operation, but many compilers won't complain).

patros
+11  A: 

Yes. All arithmetic operators in C++ are defined on int and wider. When you multiply two shorts (doesn't matter if you use * or *=) they are both converted to int first. This is covered by ISO C++ 5[expr]/9:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:

  • If either operand is of type long double, the other shall be converted to long double.
  • Otherwise, if either operand is double, the other shall be converted to double.
  • Otherwise, if either operand is float, the other shall be converted to float.
  • Otherwise, the integral promotions (4.5) shall be performed on both operands.
  • Then, if either operand is unsigned long the other shall be converted to unsigned long.
  • Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent all the values of an unsigned int, the unsigned int shall be converted to a long int; otherwise both operands shall be converted to unsigned long int.
  • Otherwise, if either operand is long, the other shall be converted to long.
  • Otherwise, if either operand is unsigned, the other shall be converted to unsigned.

[Note: otherwise, the only remaining case is that both operands are int]

and 4.5[conv.prom]:

1 An rvalue of type char, signed char, unsigned char, short int, or unsigned short int can be converted to an rvalue of type int if int can represent all the values of the source type; otherwise, the source rvalue can be converted to an rvalue of type unsigned int.

2 An rvalue of type wchar_t (3.9.1) or an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long, or unsigned long.

3 An rvalue for an integral bit-field (9.6) can be converted to an rvalue of type int if int can represent all the values of the bit-field; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bit-field. If the bit-field is larger yet, no integral promotion applies to it. If the bit-field has an enumerated type, it is treated as any other value of that type for promotion purposes.

4 An rvalue of type bool can be converted to an rvalue of type int, with false becoming zero and true becoming one.

5 These conversions are called integral promotions.

Why it only gives a warning on one line but not both is unclear, however.

Pavel Minaev
A: 

This seems to be a very weird warning in general. Some observations:

short s = 12345;
s *= 0xffff;       // no warning
s *= (int)0xffff;  // no warning
s *= 0x10000;      // C4244
s *= -0x8000;      // no warning
s *= -0x8001;      // C4244

short t = -12345;
s *= t;            // no warning
s *= (int)t;       // no warning
s *= (unsigned)t;  // no warning

s *= (int)t * 0xffff;  // no warning
s *= (int)t * 0x10000; // C4244

It doesn't seem to care about specific operators - all of +-*/ give the same results. It seems to not actually look at expression types alone, but also at literals involved, and as soon as you cross the magic threshold, the warning pops up.

Pavel Minaev