1

For the following code I get an overflow but sadly I cannot seem to understand why.

std::int8_t smallValue         = -1;
unsigned int value             = 500;
std::uint8_t anotherSmallValue = 1;

auto test = smallValue * value * anotherSmallValue;

Afterwards test is a quite large value.

Could someone explain, what happens here?

abergmeier
  • 13,224
  • 13
  • 64
  • 120
  • possible duplicate of [What happens if I assign a negative value to an unsigned variable?](http://stackoverflow.com/questions/2711522/what-happens-if-i-assign-a-negative-value-to-an-unsigned-variable) – Raymond Chen Apr 05 '13 at 12:48
  • For me the lesson here is to **always** set `-Werror=sign-conversion` – abergmeier Apr 05 '13 at 15:52

5 Answers5

6

result type will be unsigned int test here. smallValue * value smallValue will be casted to unsigned, so this expression is (unsigned)-1 * 500. However, if you compile this code with -Wsign-conversion - compiler tells you, that you do bad things. link

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • +1 for mentioning `-Wsign-conversion`. I read the _gcc_ docs for it and was shocked that it isn't enabled for `-Wconversion`. – abergmeier Apr 05 '13 at 14:54
5

When the compiler sees smallValue * value, it must decide what the data type of the result is going to be, given the input data types signed (8 bits) and unsigned int (usually, either 16 or 32 bits). The rules of C++ state that in this situation, the result will be unsigned. Therefore, the value of smallValue * value cannot be -500, as you are expecting; instead, the value -500 is interpreted as a POSITIVE number.

Furthermore, you are here multiplying an 8-bit value by a value that is typically either 16- or 32-bit. The rules of C++ in this scenario state that the smaller-storage value will first be cast to the same size as the larger; so in this case the result of smallValue * value will indeed be large enough to store a number of magnitude 500.

Proceeding to multiply by the unsigned quantity anotherSmallValue (=1) results in another unsigned with the same value.

Because you are using auto, the return type is therefore deduced to be unsigned.

Simply by casting back to a signed (by, for example, defining the value test as an int, rather than an auto, will in turn typically cast the result of the entire operation back to a signed value, without changing the bits internally; this will then properly display -500, as you expect; however, as other posters have noted, this is rather dangerous in theory because it's not techincally guaranteed to work, although it usually will work this way with today's compilers.

Dan Nissenbaum
  • 13,558
  • 21
  • 105
  • 181
2

You will get the same result with just the first two variables (smallValue * value).

First, integral promotions are applied to both values: int8_t becomes int, and unsigned int remains as it is.

Then this rule from C++11 5/9 is applied to determine the result type:

Otherwise, if the operand that has unsigned integer type has rank greater than or equal to the rank of the type of the other operand, the operand with signed integer type shall be converted to the type of the operand with unsigned integer type.

So -1 must be converted to unsigned int using modular arithmetic, giving a large positive number. Multiplying by 500 will overflow (again using modular arithmetic), giving a different large number.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

Make 'auto' into a signed type and you will be fine:

long test = smallValue * value * anotherSmallValue;
metalhead
  • 558
  • 1
  • 10
  • 24
  • That depends on what you mean by "fine". That will give the same (large positive) result if `long` is larger than `unsigned int`. If it's the same size, or you change it to `int`, then technically you'll get undefined behaviour due to signed overflow; but in practice you'll get the "expected" result of -500 on most modern computers. – Mike Seymour Apr 05 '13 at 12:57
1

Operating with mixed types is always prone to such errors. I advise you to use a single type for arithmetic operations

fatihk
  • 7,789
  • 1
  • 26
  • 48