0

I've been debugging a C++ application in VS2015 and found that a number of my double variables were ending up a NaN following a divide by zero. While this is reasonable, I have floating point exceptions enabled (/fp:except) so I would have expected this to raise an exception. Looking at the MS help page, it doesn't list what causes a floating point exception. According to this answer to a related question, divide by zero is a floating point exception. This is not the case, i.e. the following test program with /fp:except enabled

int main()
{
    try
    {
        double x = 1;
        double y = 0;
        double z = x / y;
        printf("%f\n", z);
        return 0;
    }
    catch (...)
    {
        printf("Exception!\n");
        return 0;
    }
}

displays "inf". Should this raise a floating point exception?

Edit: Checked the exceptions were enabled in the debugger and get the same result regardless enter image description here

Edit2: Further reading here on IEE 754 suggests to me that with floating point exceptions enabled I should be getting an exception. A comment to the previously linked question however states 'The name "floating point exception" is a historical misnomer. Floating point division by zero is well-defined (per Annex F/IEEE754) and does not produce any signal.'

SmacL
  • 22,555
  • 12
  • 95
  • 149
  • If you change `y` to `volatile double = 0;`, does an exception occur? The compiler may be computing `z` at compile-time as an optimization, and `volatile` should force it to perform a division at run-time. – Eric Postpischil Oct 02 '18 at 17:58
  • Still the same result with volatile double y = 0, and built with all optimizations turned off in debug mode. – SmacL Oct 03 '18 at 05:55

1 Answers1

1

Floating point exceptions are not the same as C++ exceptions. They either have to be checked using std::fetestexcept() (below) or trapped (SIGFPE). Using traps requires calling _controlfp() in MSVC and feenableexcept() in GCC.

#include <iostream>
#include <cfenv>
#include <stdexcept>

// MSVC specific, see
// https://learn.microsoft.com/en-us/cpp/preprocessor/fenv-access?view=msvc-160
#pragma fenv_access (on)

int main() {
    std::feclearexcept(FE_ALL_EXCEPT);
    double y = 0.0;
    double result{};
    result = 1 / y;
    std::cout << result << std::endl;
    if (std::fetestexcept(FE_ALL_EXCEPT) & FE_DIVBYZERO) {
        throw std::runtime_error("Division by zero");
    }
    return 0;
}

There are more examples of this in the SEI CERT C Coding Standard: https://wiki.sei.cmu.edu/confluence/display/c/FLP03-C.+Detect+and+handle+floating-point+errors.

Note that division by zero is undefined; it's not required to signal, but C and C++ implementations that define __STDC_IEC_559__ "shall" signal.

ZachB
  • 13,051
  • 4
  • 61
  • 89
  • Thanks for the answer ZachB, excuse my ignorance but what is the significance of the curly brackets in double result{}; ? – SmacL Sep 01 '21 at 10:13
  • 1
    No problem. That's called [list initialization](https://en.cppreference.com/w/cpp/language/list_initialization) and initializes the variable without allowing narrowing. Here it makes no difference, but something like `uint8_t foo{300}` would fail. See also https://stackoverflow.com/questions/18222926/why-is-list-initialization-using-curly-braces-better-than-the-alternatives. – ZachB Sep 01 '21 at 17:40
  • Thanks @ZachB, only ever used list initialization for arrays in the form int x[3] = {1,2,3}, learn something new every day! – SmacL Sep 02 '21 at 06:47