How to Capture the Integer-Divide-By-Zero Error in C++?


How to Capture the Integer-Divide-By-Zero Error in C++?

Manual Capture

The simple and straightforward method is to check it manually if possible. The following outputs “got you!”.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// https://helloacm.com
#include <iostream>
using namespace std;
 
int main() {
    try {
        int x = 1, y = 0;
        if (y == 0) {
                throw "error"; 
        }
        cout << x / y;
    } catch (...) {
        cerr << "got you!";
    }
    return 0;
}
// https://helloacm.com
#include <iostream>
using namespace std;

int main() {
    try {
        int x = 1, y = 0;
        if (y == 0) {
                throw "error"; 
        }
        cout << x / y;
    } catch (...) {
        cerr << "got you!";
    }
    return 0;
}

However, it is tedious and not-so-elegant to place checks at each possible code occurrence and we want to the program to handle this.

Try-Catch does not catch int-divide-by-zero error

The first thought would be to put the possible divide-by-zero code in side a try-catch, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
// https://helloacm.com
#include <iostream>
using namespace std;
 
int main() {
    try {
        int x = 1, y = 0;
        cout << x / y;
    } catch (...) {
        cerr << "got you!";
    }
    return 0;
}
// https://helloacm.com
#include <iostream>
using namespace std;

int main() {
    try {
        int x = 1, y = 0;
        cout << x / y;
    } catch (...) {
        cerr << "got you!";
    }
    return 0;
}

However, when you compile the above code using g++ compiler and run it, the error says Floating point exception instead of the customize error message in try-catch. This means that the error is not caught by try-catch.

Using Signal

At Linux, we can set up signals to capture the error. In this case, the error type is SIGFPE which stands for Signal Float Point Exceptions. The function to set up a signal is signal and it takes two parameters, the signal type and the function to handle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// https://helloacm.com
#include <iostream>
#include <cstdlib>
#include <csignal>
 
using namespace std;
 
void error(int) {
    cerr << "got you!";
    exit(1);
}
 
int main() {
    if (SIG_ERR == signal(SIGFPE, error)) {
        cerr << "failure to setup signal.";
        return 1;
    }
    int x = 1, y = 0;
    cout << x / y;
    return 0;
}
// https://helloacm.com
#include <iostream>
#include <cstdlib>
#include <csignal>

using namespace std;

void error(int) {
    cerr << "got you!";
    exit(1);
}

int main() {
    if (SIG_ERR == signal(SIGFPE, error)) {
        cerr << "failure to setup signal.";
        return 1;
    }
    int x = 1, y = 0;
    cout << x / y;
    return 0;
}

In short, the when SIGFPE signal is caught, the error function will be invoked to deal with the error. The exit(1) function is important here. In case if you just simply return from the error-handling-function, the disaster happens with an endless loop, which prints “got you!” repeatedly until you ctrl+C to abort/kill it.

When error happens, the OS call the stack of the error function, and set its return address just right before the int-div-by-zero. Virtually, the error-function is invoked right before the error occurs. And after the error-function returns, it will again meet the code that triggers the error, which is why it will keep printing the error.

Some might’ve thought to fix this by making the y variable the global and set to 1 inside the error function. However, this does not help. It seems that the error still occurs regardless the value of y.

1
2
3
4
5
6
// https://helloacm.com
int y = 0; // global variable
void error(int) {
    cerr << "got you!";
    y = 1; // set to 1 to fix it, but this does not work, why?
}
// https://helloacm.com
int y = 0; // global variable
void error(int) {
    cerr << "got you!";
    y = 1; // set to 1 to fix it, but this does not work, why?
}

Using Exceptions

We could easily replace above exit() with the exceptions to throw in the error function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// https://helloacm.com
#include <iostream>
#include <cstdlib>
#include <csignal>
 
using namespace std;
struct div_0_exception {};
 
void error(int) {
    throw div_0_exception();
}
 
int main() {
    if (SIG_ERR == signal(SIGFPE, error)) {
        cerr << "failure to setup signal.";
        return 1;
    }
    try {
        int x = 1, y = 0;
        cout << x / y;
    } catch (div_0_exception) {
        cout << "got you!";
    }
    return 0;
}
// https://helloacm.com
#include <iostream>
#include <cstdlib>
#include <csignal>

using namespace std;
struct div_0_exception {};

void error(int) {
    throw div_0_exception();
}

int main() {
    if (SIG_ERR == signal(SIGFPE, error)) {
        cerr << "failure to setup signal.";
        return 1;
    }
    try {
        int x = 1, y = 0;
        cout << x / y;
    } catch (div_0_exception) {
        cout << "got you!";
    }
    return 0;
}

Using Customized Signal

The above throws regardless the type of float point exceptions. The POSIX defines a more comprenhensive signal processing interface, i.e. sigaction, which is defined in csignal header.

1
2
3
4
5
6
7
// https://helloacm.com
struct sigaction;
 
int sigaction(int sig,
              struct sigaction const* restrict act,
              struct sigaction* restrict old_act
);
// https://helloacm.com
struct sigaction;

int sigaction(int sig,
              struct sigaction const* restrict act,
              struct sigaction* restrict old_act
);

Please note that, these two have the same identifies. The first one is the structure type that stores some function pointers. And the second is the actual function entry to set up the signal. The third parameter is usually set to NULL, for more information, read man 3 sigaction.

The sigaction structure has two function entry points:

1
2
void (* sa_handler)(int);
void (* sa_sigaction)(int, siginfo_t*, void*);
void (* sa_handler)(int);
void (* sa_sigaction)(int, siginfo_t*, void*);

The first entry is the light-weight handling method, which has been shown in previous code examples. The second one allows more information in the code context. Based on the second parameter, we could pass the additional information via sa_flags. Possible si_code values: FPE_INTOVF (integer overflow), FPE_FLTUND (float underflow), FPE_FLTOVF (float overflow) and what we care about – the FPT_INTDIV.

Let’s look at the complete example of C++ using comprehensive signal to catch the integer-divided-by-zero error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// https://helloacm.com
#include <iostream>
#include <cstdlib>
#include <csignal>
 
struct my_sig_action {
    typedef void (* handler_type)(int, siginfo_t*, void*);
 
    explicit my_sig_action(handler_type handler)
    {
        memset(&_sa, 0, sizeof(struct sigaction));
        _sa.sa_sigaction = handler;
        _sa.sa_flags = SA_SIGINFO;
    }
 
    operator struct sigaction const*() const
    {
        return &_sa;
    }
protected:
    struct sigaction _sa;
};
 
struct div_0_exception {};
 
void error(int sig, siginfo_t* info, void*) {
    if (FPE_INTDIV == info->si_code)
        throw div_0_exception();
}
 
int main()
{
    my_sig_action sa(error);
    if (0 != sigaction(SIGFPE, sa, NULL)) {
        cerr << "failure to setup signal.";
        return 1;
    }
    try {
        int x = 1, y = 0;
        cout << x / y;
    } catch (div_0_exception) {
        cout << "got you!";
    }
    return 0;
}
// https://helloacm.com
#include <iostream>
#include <cstdlib>
#include <csignal>

struct my_sig_action {
    typedef void (* handler_type)(int, siginfo_t*, void*);

    explicit my_sig_action(handler_type handler)
    {
        memset(&_sa, 0, sizeof(struct sigaction));
        _sa.sa_sigaction = handler;
        _sa.sa_flags = SA_SIGINFO;
    }

    operator struct sigaction const*() const
    {
        return &_sa;
    }
protected:
    struct sigaction _sa;
};

struct div_0_exception {};

void error(int sig, siginfo_t* info, void*) {
    if (FPE_INTDIV == info->si_code)
        throw div_0_exception();
}

int main()
{
    my_sig_action sa(error);
    if (0 != sigaction(SIGFPE, sa, NULL)) {
        cerr << "failure to setup signal.";
        return 1;
    }
    try {
        int x = 1, y = 0;
        cout << x / y;
    } catch (div_0_exception) {
        cout << "got you!";
    }
    return 0;
}

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
1003 words
Last Post: How to Add Search Function to A Blog?
Next Post: How to Fix - Visual Studio Reports "is not a valid value for property Width" and Fails to Start?

The Permanent URL is: How to Capture the Integer-Divide-By-Zero Error in C++?

Leave a Reply