Catching exception: divide by zero
Asked Answered
L

10

60

The following code does not catch an exception, when I try to divide by 0. Do I need to throw an exception, or does the computer automatically throw one at runtime?

int i = 0;

cin >> i;  // what if someone enters zero?

try {
    i = 5/i;
}
catch (std::logic_error e) {

    cerr << e.what();
}
Lyingin answered 25/5, 2011 at 8:32 Comment(4)
Why not just check wheter i is zero or not?Scat
Why are you trying to divide by ze— OH SHI—Quantify
No not really, I edited my question.Lyingin
This question and all the ensuing answers are a fine study in everything that's wrong with exceptions.Hinch
S
81

You will need to check it yourself and throw an exception. Integer divide by zero is not an exception in standard C++. Neither is floating point divide/remainder by zero but at least that has specific rational values that may result (such as the various NaN/Inf values).

The exceptions listed in the [stdexcept.syn] section of ISO C++20 standard (the iteration used in this answer) are:

namespace std {
    class logic_error;
        class domain_error;
        class invalid_argument;
        class length_error;
        class out_of_range;
    class runtime_error;
        class range_error;
        class overflow_error;
        class underflow_error;
}

Now you could argue quite cogently that either overflow_error (the infinity generated by IEEE754 floating point could be considered overflow) or domain_error (it is, after all, a problem with the input value) would be ideal for indicating a divide by zero.

However, section [expr.mul] specifically states (for both integer and floating point division, and integer remainder):

If the second operand of / or % is zero, the behavior is undefined.

So, it could throw those (or any other) exceptions. It could also format your hard disk and laugh derisively :-)


If you wanted to implement such a beast, you could use something like intDivEx in the following program (using the overflow variant):

#include <iostream>
#include <stdexcept>

// Integer division/remainder, catching divide by zero.

inline int intDivEx (int numerator, int denominator) {
    if (denominator == 0)
        throw std::overflow_error("Divide by zero exception");
    return numerator / denominator;
}

inline int intModEx (int numerator, int denominator) {
    if (denominator == 0)
        throw std::overflow_error("Divide by zero exception");
    return numerator % denominator;
}

int main (void) {
    int i = 42;

    try {
        i = intDivEx (10, 0);
    } catch (std::overflow_error &e) {
        std::cout << e.what() << " -> ";
    }
    std::cout << i << std::endl;

    try {
        i = intDivEx (10, 2);
    } catch (std::overflow_error &e) {
        std::cout << e.what() << " -> ";
    }
    std::cout << i << std::endl;

    return 0;
}

This outputs:

Divide by zero exception -> 42
5

You can see it throws and catches the exception (leaving the return variable untouched) for the divide by zero case.

Stedfast answered 25/5, 2011 at 8:39 Comment(12)
Thanks, I was looking for the cases when system throws an exception, are these possible in c++?Lyingin
@user33424, yes it's possible, see my answer for example on std::bad_alloc which is thrown by newGill
hmm so you don't have to choose carefully when you decide to use an exception class as they all take same parameter.Lyingin
@user But you should choose one that makes your intention clear about what went wrong. Throwing bad_alloc for e.g. invalid_argument is bad design.Elmer
As the domain of a function is the set of valid inputs for which the function is defined, wouldn't domain_error be more appropriate to throw here?Satsuma
I belive that std::domain_error is the correct one to throw here, since the division a / b is well defined except in the domain b = 0.Arthurarthurian
@Arthurarthurian (and John H), I suspect I chose overflow (hard to tell since it was many years ago) because domain_error is a type of logic_error, which is usually concerned with stuff you should be able to detect at compile time. Not sure if divide-by-zero is covered in that since you may not know the denominator is zero until run-time. However, I've added that in as another possibility.Stedfast
+1 In Java, there is ArithmeticException that inherits from RuntimeException. So in my view, the best match would be std::runtime_error because there is no ArithmeticException equivalent in C++Vaientina
@Hani, I concur. However, just keep in mind we are able to create non-standard exceptions that inherit from those standard ones. In other words, we could create a sub-class of std::runtime_error called not_std::arithmetic_error, and use that. The only restriction is that we couldn't/shouldn't place it into std.Stedfast
In my code C++17, x/0 return nan, no exception.Loosetongued
@KeminZhou: I'm not sure what point you're making there. The answer states that no exceptions are thrown for divide by zero.Stedfast
I want to draw the attention of the special value nan instead of throwing an exception.Loosetongued
H
30

Updated with comments from ExcessPhase

GCC (at least version 4.8) will let you emulate this behaviour:

#include <signal.h>
#include <memory>
#include <iostream>

int main() {
    std::shared_ptr<void(int)> handler(
        signal(SIGFPE, [](int signum) {throw std::logic_error("FPE"); }),
        [](__sighandler_t f) { signal(SIGFPE, f); });

    int i = 0;

    std::cin >> i;  // what if someone enters zero?

    try {
        i = 5/i;
    }
    catch (std::logic_error e) {
        std::cerr << e.what();
    }
}

This sets up a new signal handler which throws an exception, and a shared_ptr to the old signal handler, with a custom 'deletion' function that restores the old handler when it goes out of scope.

You need to compile with at least these options:

g++ -c Foo.cc -o Foo.o -fnon-call-exceptions -std=c++11

Visual C++ will also let you do something similar:

#include <eh.h>
#include <memory>

int main() {
    std::shared_ptr<void(unsigned, EXCEPTION_POINTERS*)> handler(
        _set_se_translator([](unsigned u, EXCEPTION_POINTERS* p) {
            switch(u) {
                case FLT_DIVIDE_BY_ZERO:
                case INT_DIVIDE_BY_ZERO:
                    throw std::logic_error("Divide by zero");
                    break;
                ...
                default:
                    throw std::logic_error("SEH exception");
            }
        }),
        [](_se_translator_function f) { _set_se_translator(f); });

    int i = 0;

    try {
        i = 5 / i;
    } catch(std::logic_error e) {
        std::cerr << e.what();
    }
}

And of course you can skip all the C++11-ishness of this and put them in a traditional RAII-managing struct.

Haze answered 1/9, 2014 at 7:48 Comment(12)
Setting and restoring the signal handler should use RAII! Also you cannot assume, that the default signal handler was the one you temporarily replaced.Juna
Thanks, @ExcessPhase, I've updated the answer to reflect this.Haze
While compiling the visual c++ code in visual studio 2012, I am getting this error: "FLT_DIVIDE_BY_ZERO' : undeclared identifier". I have included "windows.h". Where am I going wrong ?Digenesis
I've fixed the GCC version. I don't have a VC++ install to hand to test it.Haze
it is better in C++ include <csignal> and call std::signalKhania
And why do U use shared_ptr? As I understand to uninstall handlers with RAII. But if U don't share this pointer, it should be better use unique_ptr, or even custom RAII struct.Khania
@kyb: I'm not really sure what std::signal adds here (or anywhere, for that matter). But you might well be right that unique_ptr would be a slightly better choice than shared_ptr.Haze
Isn't FLT_DIVIDE_BY_ZERO and SIGFPE just for floating-point exceptions?Fennelly
@Fennelly Isn't this question about catching divide-by-zero?Haze
@Haze yes, it is about catching divide-by-zero, but what I'm trying to say is I thought FLT_DIVIDE_BY_ZERO and SIGFPE were just for floating-point exceptions and didn't think that integer division by zero (the subject of this post) could be caught by either of these two signals.Fennelly
Ah, I see what you mean, but no, SIGFPE is for integer divide-by-zero too. Single Unix Specification defines SIGFPE as "Erroneous arithmetic operation." FLT_DIVIDE_BY_ZERO is indeed for floating-point-only and there is a corresponding INT_DIVIDE_BY_ZERO; I'll update the answer.Haze
I've tried this code both in online compiler and docker with gcc:10. Tried std::signal as well. But in any case exception that is thrown from signal handler aborts application and is not catched. Only option that worked for me is using longjmp in signal handler.Stoneware
C
11

As far as I know C++ specifications does not mention anything about divide by zero exeption. I believe you need to do it yourself...

Stroustrup says, in "The Design and Evolution of C++" (Addison Wesley, 1994), "low-level events, such as arithmetic overflows and divide by zero, are assumed to be handled by a dedicated lower-level mechanism rather than by exceptions. This enables C++ to match the behaviour of other languages when it comes to arithmetic. It also avoids the problems that occur on heavily pipelined architectures where events such as divide by zero are asynchronous."`

Carduaceous answered 25/5, 2011 at 8:37 Comment(0)
P
2

You need to throw the exception manually using throw keyword.

Example:

#include <iostream>
using namespace std;

double division(int a, int b)
{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}

int main ()
{
   int x = 50;
   int y = 0;
   double z = 0;

   try {
     z = division(x, y);
     cout << z << endl;
   }catch (const char* msg) {
     cerr << msg << endl;
   }

   return 0;
}
Pint answered 7/6, 2015 at 21:23 Comment(1)
Or, alternatively, you can throw a more appropriate exception type, such as std::invalid_argument or std::domain_errorPitre
P
2

setjmp + longjmp

https://mcmap.net/q/23905/-catching-exception-divide-by-zero mentioned the possibility or throwing a C++ exception from a signal handler, but Throwing an exception from within a signal handler mentions several caveats of that, so I would be very careful.

As another potentially dangerous possibility, you can also try to use the older C setjmp + longjmp mechanism as shown at: C handle signal SIGFPE and continue execution

main.cpp

#include <csetjmp>
#include <csignal>
#include <cstring>
#include <iostream>

jmp_buf fpe;

void handler(int signum) {
    longjmp(fpe, 1);
}

int main() {
    volatile int i, j;
    for(i = 0; i < 10; i++) {
        struct sigaction act;
        struct sigaction oldact;
        memset(&act, 0, sizeof(act));
        act.sa_handler = handler;
        act.sa_flags = SA_NODEFER | SA_NOMASK;
        sigaction(SIGFPE, &act, &oldact);
        if (0 == setjmp(fpe)) {
            std::cout << "before divide" << std::endl;
            j = i / 0;
            sigaction(SIGFPE, &oldact, &act);
        } else {
            std::cout << "after longjmp" << std::endl;
            sigaction(SIGFPE, &oldact, &act);
        }
    }
    return 0;
}

Compile and run:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

Output:

i = 0
before divide
after longjmp
i = 1
before divide
after longjmp
i = 2
before divide
after longjmp

man longjmp says that you can longjmp from signal handlers, but with a few caveats:

POSIX.1-2008 Technical Corrigendum 2 adds longjmp() and siglongjmp() to the list of async-signal-safe functions. However, the standard recommends avoiding the use of these functions from signal handlers and goes on to point out that if these functions are called from a signal handler that interrupted a call to a non-async-signal-safe function (or some equivalent, such as the steps equivalent to exit(3) that occur upon a return from the initial call to main()), the behavior is undefined if the program subsequently makes a call to a non-async-signal-safe function. The only way of avoiding undefined behavior is to ensure one of the following:

  • After long jumping from the signal handler, the program does not call any non-async-signal-safe functions and does not return from the initial call to main().

  • Any signal whose handler performs a long jump must be blocked during every call to a non-async-signal-safe function and no non-async-signal-safe functions are called after returning from the initial call to main().

See also: Longjmp out of signal handler?

However Throwing an exception from within a signal handler mentions that this has further dangers with C++:

setjmp and longjmp aren't compatible with exceptions and RAII (ctors/dtors), though. :( You'll probably get resource leaks with this.

so you would have to be very very careful with that as well.

I guess the moral is that signal handlers are hard, and you should avoid them as much as possible unless you know exactly what you are doing.

Detect floating point zero division

It is also possible to detect floating point division by zero with a glibc call to:

#include <cfenv>

feenableexcept(FE_INVALID);

as shown at: What is the difference between quiet NaN and signaling NaN?

This makes it raises SIGFPE as well like the integer division by zero instead of just silently qnan and setting flags.

Petrarch answered 13/4, 2019 at 11:40 Comment(0)
H
1

You should check if i = 0 and not divide then.

(Optionally after checking it you can throw an exception and handle it later).

More info at: http://www.cprogramming.com/tutorial/exceptions.html

Houdini answered 25/5, 2011 at 8:36 Comment(0)
L
0

What about this one? Tested with Clang, GCC throws SIGILL.

#include <iostream>
#include <cassert>

int main()
{
    unsigned int x = 42;
    unsigned int y = x;
    y -= x;
    x /= y;
    
    std::cout << x << " != "<< *(&x) << std::endl;
    assert (*(&x) == x);
}
Later answered 8/3, 2021 at 16:0 Comment(0)
S
0

If you want to capture division by zero error involving integers (it's valid with float) without having to trigger it with a throw, you should use a signal handler like this:

void signalHandler( int signum ){
    //Division by 0 is c++ signal #8 (signum = 8).         
    cout << "Interrupt signal (" << signum << ") received.\n";
    exit(signum);
}

Then define this function as your signal handler before the code where division by zero may occur, like this:

int main(){
   ...
   signal(SIGFPE, signalHandler);
   ...
}

Note that SIGFPE is the signal for invalid arithmetic operation.

Stylopodium answered 17/5, 2023 at 13:49 Comment(2)
It's not generally safe to call something like std::cout in a signal handler. This is because std::cout may allocate, and may raise an exception, both of which are UB inside a signal handler. en.cppreference.com/w/cpp/utility/program/signalPitre
You're right. My sample code was just for illustration purposes that can be readily tested. It can be useful also during development stage.Stylopodium
G
-3

do i need to throw an exception or does the computer automatically throws one at runtime?

Either you need to throw the exception yourself and catch it. e.g.

try {
  //...
  throw int();
}
catch(int i) { }

Or catch the exception which is thrown by your code.

try {
    int *p = new int();
}
catch (std::bad_alloc e) {
    cerr << e.what();
}

In your case, I am not sure if is there any standard exception meant for divide by zero. If there is no such exception then you can use,

catch(...) {  // catch 'any' exception
}
Gill answered 25/5, 2011 at 8:43 Comment(2)
Division by zero is undefined behavior.Ferromagnetic
people, which do not know about this features existence (in Windows since more than 20 years and OSX) should not be answering here, nor voting nor calling themselves software engineers.Juna
D
-5

You can just do assert(2 * i != i) which will throw an assert. You can write your own exception class if you need something fancier.

Dinny answered 14/7, 2012 at 22:29 Comment(2)
-1 I really dislike this solution. How is that easier than assert(i != 0)? I did not think through the border cases, but if it's not trivial to see that an assertion is stated correctly, then you should not put it.Haviland
In addition, assert is frequently deactivated in production code due to the presence of NDEBUG - assert() is typically a development-only method for catching problems. In any case, throwing an assertion is not throwing an exception. The former will abort your program rather than generate something you can catch.Stedfast

© 2022 - 2024 — McMap. All rights reserved.