Code Yarns ‍👨‍💻
Tech BlogPersonal Blog

Math errors in C++ and how to prevent or catch them

📅 2015-Jan-05 ⬩ ✍️ Ashwin Nanjappa ⬩ 🏷️ cpp, error, math ⬩ 📚 Archive

Math functions provided by the standard C++ library are defined in the cmath header file. Many of these functions require their input values to be in a specific domain. For example, the log function requires its input to be greater than zero. log(-3.14) or log(0) is not defined mathematically. The result of such a call is set to a special NaN, inf or -inf value. However, no exception is thrown and the call has actually failed silently. Note that this does not affect the correctness of future math function calls.

Preventing math error

There is no warning option provided by GCC that can be enabled to detect such errors in the code. Nor is there any compiler option to enable the standard C++ library to thrown an exception, say std::domain_error, when such an error occurs. Other libraries, like Boost, do throw such exceptions.

The only way to prevent these math errors is to check the bounds of the inputs passed to these functions. For example, you can write a wrapper function for each of such math functions that does the bounds checking and throws error or exception when the input is wrong. You can change your code to explicitly use only these math wrapper functions instead of those from cmath.

If the performance of this math code is important to you, then inline this function and enable the bounds checking only in Debug mode or when you want to check the correctness of some inputs. Disable the checking for Release mode.

Catching math error

When a math function is executed with wrong input, the errno variable from the cerrno header file is set to non-zero value. The list of error values can be seen here. Specifically, for math errors, it might be set to either ERANGE or EDOM. This refers to a range or domain error respectively. To get the error string explaining the error, use the std::strerror function.

For example:

// Checking for math error in C++

#include <cerrno>
#include <cmath>
#include <cstring>
#include <iostream>

std::cout << log(0) << std::endl;
if (errno)
    std::cout << std::strerror(errno) << std::endl;
if (errno == EDOM)
    std::cout << "Domain error\n";
if (errno == ERANGE)
    std::cout << "Range error\n";

Reference: Section 40.3, The C++ Programming Language (4 Ed) by Stroustrup

Tried with: GCC 4.9.2 and Ubuntu 14.04

© 2022 Ashwin Nanjappa • All writing under CC BY-SA license • 🐘📧