-2

So we are working on an exercise for Uni and we can't figure out why this code outputs the second value as -1 We believe that it is due to the 16-bit limit but don't understand exactly why and can't find any sources on this issue since we don't know what it actually is. I'm sorry if this seems really stupid, please help D:

#include <stdio.h>
#include <stdint.h>

int main() {
  int16_t y = 1024, z = 65; // auch "short" als Datentyp verwendbar

  y = y * z;
  printf("1. Ausgabe: %d\n", y);
  printf("2. Ausgabe: %d\n", y / 3 * 3 - 3 * y / 3);
  printf("\n");

  return 0;
}

we expected the result to be 0 for 2. Ausgabe but it outputs -1

Adrien G.
  • 411
  • 3
  • 15
  • 2
    Suggestion: break it up into smaller parts by introducing extra variables, then examine these variables individually. – dandan78 Nov 04 '19 at 11:04
  • 2
    Hint: Use a debugger or try printing intermediate values. For example `y`, `y / 3`, `y / 3 * 3` etc in hex and convert them to 16 bit binary. Understand rounding in `C`. For example what is the output of `i / 3 * 3` for `i = 0, 1, 2, .... 15`. `signed` overflow is undefined behavior in C. – Gyapti Jain Nov 04 '19 at 11:04
  • 1
    Does this answer your question? [What is the behavior of integer division?](https://stackoverflow.com/questions/3602827/what-is-the-behavior-of-integer-division) – Gyapti Jain Nov 04 '19 at 11:07
  • You are also using the wrong format specifiers for `int16_t`. Strictly speaking it should be `"%"PRIi16` from inttypes.h. – Lundin Nov 04 '19 at 11:55

3 Answers3

1

the range for a int16_t is −32,768 ... 32,767

y * z = 1024*65 = 66560 but hence will be stored as 66560 % 2^16 = 1024 so you still have y = 1024 and your statement y = y * z is useless

y / 3 * 3 = (y / 3) * 3 = 341 * 3 = 1023 != y because of rounding

3 * y / 3 = (3 * y) / 3 = y because there is no rounding

when you substract you get -1

the problem is you're overflowing your variable and doing integer divisions

use floats instead of int16_t

Wheatley
  • 162
  • 1
  • 7
  • 1
    You missed the statement `y = y * z`. – gustgr Nov 04 '19 at 11:07
  • @Wheatley You shouldn't rely on overflowing signed integers. It's undefined behaviour. The result can be anything. – JUSHJUSH Nov 04 '19 at 11:29
  • @JUSHJUSH Assuming `int` is 32 bits, then `y * z` never overflows a signed integer and there is no UB anywhere. Converting a large signed integer to a smaller one is implementation-defined, not undefined. – Lundin Nov 04 '19 at 11:57
0

int16_t has the range [−32768, +32767] - you are overflowing that range immediately with the first multiplication.

What does the first printf print out? That should have been an indication. You also mention it in your question.

Google int16_t range and it will show you the range above. Try using an int32_t and see what difference that makes.

Salgar
  • 7,687
  • 1
  • 25
  • 39
0

Given y = y * z; where all operands are type int16_t and the values are 1024 * 65 = 66560, then there are two possibilities:

  • If your system is 8 or 16 bit, you will have 16 bit int type. There will be no type promotion of the operands since they are already 16 bits and you get a signed integer overflow, invoking undefined behavior. Since 16 bit signed integers can only store values up to 32767.
  • If your system is 32 bit, then both y and z are implicitly promoted (see Implicit type promotion rules) to type int which is then 32 bits. The result of the multiplication is of type int. The value 66560 will fit just fine.

    You then convert this 32 bit int to int16_t upon assignment. The value will no longer fit - what will happen is an implementation-defined conversion from signed 32 to signed 16. (In theory your system may raise a signal here.)

    In practice most systems will simply take the 66560 = 10400h and cut off the MS bytes, leaving you with 1024 = 400h.

In either case, the equation y = y * z; is highly questionable given the size of the input values! This is to be regarded as a bug. You should use uint16_t or int32_t instead.


As for y / 3 * 3 - 3 * y / 3, it will be 1024 / 3 * 3 - 3 * 1024 / 3. All operands are integers and the operator associativity of multiplicative operators * and / is left-to-right.

So you get 341 * 3 - 3072 / 3 -> 1023 - 1024 = -1.


As a side-note, you are using the wrong printf conversion specifier. The most correct one for int16_t is this:

#include <inttypes.h>

printf("1. Ausgabe: %"PRIi16 "\n", y);
Lundin
  • 195,001
  • 40
  • 254
  • 396