Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fixpoint-Overflow-Traps for int16_t and int8_t

Tags:

c

gcc

ubuntu

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.

like image 543
skn Avatar asked Sep 12 '17 08:09

skn


2 Answers

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.

like image 80
Lundin Avatar answered Oct 20 '22 12:10

Lundin


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.

like image 33
unwind Avatar answered Oct 20 '22 13:10

unwind