3
int32_t result = registers[rs] + registers[rt];  
if (((registers[rs] > 0) && (registers[rt] > 0) && (result < 0)) || 
    ((registers[rs] < 0) && (registers[rt] < 0) && (result > 0))) {
        fprintf(stderr, "arithmetic overflow\n");
} else {
        registers[rd] = result;
}

Just a simple add and detect the overflow, if registers[rs] is 2147483647 and registers[rt] is 1, registers[rd] will have a result -2147483648. But if I add a printf("%d\\n", result) after declaring the variable result with same value in rs and rt, it will display arithmetic overflow

when registers[rs] is 2147483647 and registers[rt] is 1, it should output the arithmetic overflow

10
  • 5
    What you describe is typical of undefined behavior. Please edit your question to show us a minimal reproducible example. And tell us what efforts of debugging you have done. Commented Nov 11, 2024 at 14:59
  • 1
    Regarding undefined behavior, signed integer arithmetic overflow is one thing that leads to undefined behavior (or UB for short). And it happens already when calculating the value for result, after that it's too late. Once you have UB, all bets are off. Commented Nov 11, 2024 at 15:00
  • You need to test for overflow before calculating the final result ... assuming a and b are greater than 0 ==> a+b > INT_MAX ==> a > INT_MAX - b ==> if (a > INT_MAX - b) DONOTADD(); else OkToAdd();
    – pmg
    Commented Nov 11, 2024 at 15:02
  • 1
    This question is similar to: Detecting signed overflow in C/C++. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Nov 11, 2024 at 15:06
  • 1
    @SvenNilsson Indeed, this could have been made well-defined behavior in C23 when they finally tossed out exotic signedness from the language. But instead they rolled out a library to check for overflow, stdchkdint.h... which is good and all, but there's no reason this should be UB either. Because on every ISA I know, signed overflow is well-defined. C should be prepared for working on real-world computers instead of fictional computers...
    – Lundin
    Commented Nov 11, 2024 at 15:29

2 Answers 2

8
int32_t result = registers[rs] + registers[rt];  

When signed integer overflow occurs it invokes UB(Undefined Bahaviour).

You need to check before you add those integers.

bool willOverflow(int32_t a, int32_t b) 
{
    bool overflow = false;

    if (a > 0 && b > 0 && (a > INT32_MAX - b))  
    {
        overflow = true;
    }
    else if (a < 0 && b < 0 && (a < INT32_MIN - b)) 
    {
        overflow = true;
    }

    return overflow;
}
if (willOverflow(registers[rs], registers[rt]))
{
    fprintf(stderr, "arithmetic overflow\n");
} 
else 
{
    registers[rd] = registers[rs] + registers[rt];
}
5
  • 1
    Or, in C23, use the type-generic maco ckd_add() from header <stdckdint.h> to detect overflow. Commented Nov 11, 2024 at 15:40
  • @JonathanLeffler I am writing about currently supported standards. BTW do you know what the C23 GCC status is? Commented Nov 11, 2024 at 16:04
  • 1
    According to my test (code shown here), stdckdint.h is fully supported in GCC-14 and Clang-18 (but maybe also earlier versions) so they are clearly "currently supported stsndards". Commented Nov 11, 2024 at 16:20
  • @WeijunZhou I am not asking about this include. Commented Nov 11, 2024 at 16:59
  • 2
    Quite a lot of C23 is supported by GCC 14. See Changes in GCC 14 which says "Some more C23 features have been implemented" and specifically cites <stdckdint.h>. See also Changes in GCC 13 which lists previous changes for C23 support — things like nullptr and nullptr_t. I expect the status for Clang is similar. Commented Nov 11, 2024 at 17:18
3

If you have a potentially overflowing signed integer operation, you need to check if it overflows before performing the operation. Otherwise, if it overflows, your program will have undefined behavior and it could do or print just about anything.

In C23, ckd_add, ckd_sub and ckd_mul was added which checks for overflow before doing the addition, subtraction or multiplication:

#include <inttypes.h>
#include <stdckdint.h> // chd_add, ckd_sub, ckd_mul
#include <stdint.h>
#include <stdio.h>

void foo() {
    // ...

    int32_t result;
    if (ckd_add(&result, registers[rs], registers[rt])) {
        fputs("arithmetic overflow\n", stderr);
    } else {
        printf("All's well, the result is %" PRId32 "\n", result);
    }    
}

If you can't use C23, you could make your own function that checks before adding:

bool add_int32(int32_t *result, int32_t lhs, int32_t rhs) {
    // check if overflow would happen _before_ letting it happen:
    if ((rhs > 0 && INT32_MAX - rhs < lhs) ||
        (rhs < 0 && INT32_MIN - rhs > lhs))
    {
        return true; // overflow would happen
    } else {
        *result = lhs + rhs;
        return false;
    }
}

Note that the above hand-made function only handles int32_ts while the standard ckd_add is a macro using the _Generic operator to handle all integer types (other than plain char, bool and enumeration types) in any combination, as-if it was a function with the signature:

bool ckd_add(int_type1 *result, int_type2 x, int_type3 y);

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.