0

I am trying to use signaling_nan() to automatically throw an exception if a variable whose value will be set post-initialization, is used before being set.

I have tried using the floating point exception functionalities via fenv.h, however that solution needs an if test for FE_INVALID, which I would like to avoid.

I am including a reduced example code below. In the code I have a class A with a member double x, which I initialize to a signaling_nan() in the constructor. The class also includes a function divide_by_x, which divides the argument passed in by the class member x. I create an object A a1, and call a1.divide_by_x(10.0). I expected the program to thrown an exception, however it proceeds and returns nan as a value for the divide_by_x function call.

In the example code I have included an if test for the FE_INVALID floating point exception at the end of the program, and its output changes along expected lines if x is initialized with a quiet_nan() [Not invalid] vs signaling_nan() [FE_INVALID]. This indicates that the signaling_nan() is indeed generating a floating point exception, but the program continues executing.

#include<iostream>
#include <utility>
#include <limits>
#include <float.h>
#include <signal.h>
#include <fenv.h>

using namespace std;

class A
{
public:

  // Constructor
  A():x(std::numeric_limits<double>::signaling_NaN()){}
  //A():x(std::numeric_limits<double>::quiet_NaN()){}

  // Set x
  void set_x(double x_value) { x = x_value; }

  // Divide number by x
  double divide_by_x(double other_number)
  {
    double result = other_number/x;

    return result;
  }
  
  // Member
  double x;
};
  
int main(void)
{
  feclearexcept(FE_ALL_EXCEPT);
  
  A a1;

  double before_setting_x;

  before_setting_x = a1.divide_by_x(10.0);

  if (fetestexcept(FE_INVALID))
        printf("FE_INVALID\n");
    else
        printf("Not invalid\n");
  
 return 0;
}

Output with x initialized with signaling_nan():

FE_INVALID

Output with x initialized with quiet_nan():

Not invalid

PAS
  • 13
  • 3
  • You can write a wrapper, ideally, which will throw if a float-point exception for a signalling NaN is raised. Any other approach will be platform-specific, and likely to be of fairly dubious quality. Any assembly approach would be architecture specific, and even on just x86 you have to deal with x87 exceptions, SSE2, and more. You could also use a GCC extension, `__attribute__ ((constructor))` to invoke `feenableexcept` at startup, which will invoke `SIGFPE` if the errors occur (such as `FE_INVALID`. – Alex Huszagh Jul 02 '21 at 22:51
  • You could then combine this with a signal handler to then ensure you catch this error, which could then throw a C++ exception (or you could properly handle it). Not really ideal. The best approach, if you want to stop program execution on a signalling NaN, would just be to enable `feenableexcept` and have no handling logic. – Alex Huszagh Jul 02 '21 at 22:52
  • *...without needing if tests?* Probably can't be done without an `if` test (or using trickery to obfuscate the `if` test). – Eljay Jul 03 '21 at 12:56

1 Answers1

1

So, there's a lot of ways to go about this problem:

Custom Classes, Which Check for FPE in Operations

The simplest way is just to write a wrapper class, that checks for floating-point exceptions, and then never have to deal with it later, with all the numeric operations built-in.

#include <exception>
#include <fenv.h>

class FloatingPointException: public std::exception {
    const char* what() const throw() {
        return "Exception raised in floating-point operation.";
    }
};

class F64 {
public:
    F64(double f): m_f(f) {}
    F64(const F64 &other): m_f(other.m_f) {} 
    F64(F64 &&other): m_f(other.m_f) {}
    F64& operator=(const F64& other) { m_f = other.m_f; return *this; }
    F64& operator=(F64&& other) { m_f = other.m_f; return *this; }

    operator double() const { return m_f; }

    // This is where we use fenv.
    F64 operator*(F64 other) const {
        feclearexcept(FE_ALL_EXCEPT);
        auto result = m_f * other.m_f;
        if (fetestexcept(FE_INVALID)) {
            throw FloatingPointException();
        }
        return F64(result);
    }

    F64& operator*=(F64 other) {
        operator=(operator*(other));
        return *this;
    }

    // This is where we use fenv.
    F64 operator/(F64 other) const {
        feclearexcept(FE_ALL_EXCEPT);
        auto result = m_f / other.m_f;
        if (fetestexcept(FE_INVALID)) {
            throw FloatingPointException();
        }
        return F64(result);
    }

    F64& operator/=(F64 other) {
        operator=(operator/(other));
        return *this;
    }

    // Implement all other operations.
private:
    double m_f;
};

feenableexcept

Another approach is to use feenableexcept at the start of the program:

#include <fenv.h>
int main() {
    feenableexcept(FE_INVALID);
    ...
    return 0;
}

This will produce a SIGFPE if an invalid floating-point exception occurs. You can also use a GCC extension so this automatically occurs before main.

#define _GNU_SOURCE 1
#include <fenv.h>
static void __attribute__ ((constructor)) trapfpe {
    feenableexcept(FE_INVALID);
}

You can also use this with signal handlers.

Bespoke Assembly

I highly recommend not doing this, since it won't be portable (even on x86, since it would differ for x87 FPUs vs. newer x86 CPUs with SSE2. However, you could try to use inline assembly to check for floating-point exceptions, even though this is likely a very bad idea. For example, dealing with the x87 FPU status register is detailed here.

Alex Huszagh
  • 13,272
  • 3
  • 39
  • 67
  • 1
    Thank you Alexander for your multifarious response. I used the feenableexcept approach, and it worked for both my example code and the broader development scenario I was considering. – PAS Jul 03 '21 at 00:58