7

I have a complicated program with fixed-point arithmetic.

It seems that there are occasional overflows.

To find them I have set the flag -ftrapv. This seems to work only with int32_t. Is that correct?

Is there a way to achieve the same behavior with int16_t and int8_t?

Here is my test code:

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

int main(void)
{
    int8_t int8 = 127;
    int8 += 1;
    printf("int8: %d\n",int8);

    int16_t int16 = 32767;
    int16 += 1;
    printf("int16: %d\n",int16);

    int32_t int32 = 2147483647;
    int32 += 1;
    printf("int32: %d\n",int32);
}

I compile with:

rm a.out; gcc -ftrapv main.c && ./a.out

and get:

int8: -128
int16: -32768
Aborted

My compiler version is gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609.

Note: Some of the answers refer to a test program that I have incorrectly written before.

skn
  • 169
  • 1
  • 7
  • 3
    I don't think your results are correct, because types smaller than int will always be promoted to int, so those int8 and int16 operations don't overflow – phuclv Sep 12 '17 at 09:00
  • @ Lưu Vĩnh Phúc: Yes, see also Lundin's answer – skn Sep 12 '17 at 09:34
  • 1
    Voting to close this since the question has changed and it now invalidates posted answers. – Lundin Sep 12 '17 at 12:20

3 Answers3

7

I have no idea what you are trying to do.

  • The maximum value a int8_t can hold is 127 and not 255.
  • The maximum value a int16_t is 32767 and not 65535.
  • The maximum value a int32_t can hold is indeed 2147483647.

So this is what your code does:

  • int8_t int8 = 255; Assign the value 255 to a variable that can hold a maximum of 127. It won't fit, invoke implicit conversion in some implementation-defined manner. Most likely you end up with the value -1. This is an implicit lvalue conversion and no signed integer overflow.

  • printf("int8: %d\n",int8 + 1); Print the result of -1 + 1. It is 0. No overflow anywhere.

  • The very same thing happens with the 16 bit variable, implicit conversion, end up with value -1, print a 0.

  • int32_t int32 = 2147483647; This line is different from the other two, as you actually set the int32 to the maximum value it can contain. If you take +1 on that, you do get a signed integer overflow which invokes undefined behavior.

On top of that, the two smaller integer types can't overflow upon the addition, even if that was what your code was doing (which it doesn't). Both operands would get integer promoted to type int - there would be no overflow. See Implicit type promotion rules for a detailed explanation about how this works.

Lundin
  • 195,001
  • 40
  • 254
  • 396
5

I don't think so, since because of C's default integer promotion rules, the arithmetic really doesn't happen as the smaller types.

For instance (from the C11 draft, §5.1.2.3 11):

EXAMPLE 2
In executing the fragment

char c1, c2;
/* ... */
c1 = c1 + c2;

the "integer promotions" require that the abstract machine promote the value of each variable to int size and then add the two ints and truncate the sum.

This is frequently the cause of confusion in code like:

uint8_t x;

x = get_some_byte();
x |= 1;

The last line is really the equivalent of:

x = x | 1;

and the right-hand side will be promoted to int, so the assignment "back" to uint8_t risks truncating which some tools warn you about.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    Even if there was no implicit promotion of the operands, there would be no overflow, because `-1 + 1` is well-defined. The problem isn't so much the implicit promotion, but rather the OP assigning the wrong values to the variables. – Lundin Sep 12 '17 at 09:25
-1

I have found the following minimally invasive method for myself:

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

#ifdef INTOF_CHECK
#define INTOF_ERROR (printf("line %d: loss of bits\n", __LINE__) & 0)
#define i8(v) ((v) > 127 || (v) < -127 ? INTOF_ERROR : (v))
#define i16(v) ((v) > 32767 || (v) < -32768 ? INTOF_ERROR : (v))
#else
#define i8(v) (v)
#define i16(v) (v)
#endif

int main(void)
{  
    int8_t int8 = i8(127);
    printf("int8: %d\n",i8(int8 + 1));

    int16_t int16 = i16(32767);
    printf("int16: %d\n",i16(int16 + 1));
}

Compile with:

rm -f a.out; gcc main.c -DINTOF_CHECK && ./a.out

The output is:

line 16: loss of bits
int8: 0
line 19: loss of bits
int16: 0
skn
  • 169
  • 1
  • 7