1

I've got some piece of code calculating a vector of functions of a vector of independent variables (and parameters):

struct instance
{    double m_adParam1, m_adParam2;
     std::array<double, OUTSIZE> calculate(const std::array<double, INSIZE>&x) const;
};

When any part of this code fails due to a division by (nearly) zero or due to a domain error, the entire code should fail and tell the caller, that it cannot calculate this function for the given input.

This should also apply for calculated values used for comparison only. So testing the result vector for NAN or infinity is not sufficient. Inserting tests after every fallible operation is infeasible.

How could one do this?

I read about fetestexcept(). But it is unclear what is resetting this state flag. SIGFPE is useless for anything other than debugging. Having the ability to convert this into C++ exceptions as on Windows would be great, but inserting a final check would be sufficient as well.

Inserting code to check for invalid inputs for the following code is also infeasible, as it would slow down the calculation dramatically.

Frank Puck
  • 467
  • 1
  • 11
  • 4
    Shouldn't you check *before* any of those operations if the domain constraints are satisfied and eventually signal the error? – Bob__ Feb 10 '23 at 19:43
  • 1
    `SIGFPE` is not an exception. This is a part of OS. C++ exceptions are a part of the language. – 273K Feb 10 '23 at 19:44
  • 1
    Despite having the same name, OS exceptions and C++ exceptions are two entirely different things. – Pete Becker Feb 10 '23 at 19:49
  • 1
    You can create your own `safe_double` class that wraps a `double` and will throw an exception if your error condition is meet in the operation. That said, that makes every one of the operations checked, which might be even worse performance then checking if the domain of the input is correct. – NathanOliver Feb 10 '23 at 19:52
  • 1
    @273K: `SIGFPE` is an OS signal delivered in response to a hardware *exception*, if you unmask floating point exceptions. By default, all FP exceptions are masked, so they're just recorded in MXCSR (which is what `fetestexcept` checks.) Anyway, if your code unmasks FP exceptions and then triggers one, you get a `SIGFPE`. It's not wrong to call it an exception, and this question does ask about converting it to a C++ exception, correctly making the distinction between HW FP exceptions and C++ exceptions. (IDK how possible it would be to write a SIGFPE handler that throws to the parent thread) – Peter Cordes Feb 10 '23 at 21:07
  • Assuming you use glibc, does `feenableexcept( FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );` do what you want? – chtz Feb 11 '23 at 00:01
  • @Frank Puck Given "When any part of this code fails due to a division by (nearly) zero ... the entire code should fail", why should division by **nearly** zero (and not zero) fail? What is wrong about a +/- infinity quotient? – chux - Reinstate Monica Feb 11 '23 at 05:18
  • 1
    With regard to throwing from inside a signal handler, see https://stackoverflow.com/questions/1717991/throwing-an-exception-from-within-a-signal-handler. There's a note that MSVC++ supports it, and that gcc with `-fnon-calll-exceptions` might work on some platforms. I have not checked. – Nate Eldredge Feb 12 '23 at 18:10
  • 1
    In C the most portable approach is `setjmp/longjmp` but this does not interact well with C++, since it will not unwind the stack; if you `longjmp` out of the scope of some objects, their destructors will not be called. It might work if you keep the `setjmp` close enough to the calculation code that no objects are (nontrivially) constructed in between, and you could then `throw` when the setjmp returns for the second time. – Nate Eldredge Feb 12 '23 at 18:12

1 Answers1

2

I read about fetestexcept(). But it is unclear what is resetting this state flag.

That is what feclearexcept() does. So you can do something like

std::feclearexcept(FE_INVALID | FE_OVERFLOW | FE_DIVBYZERO);

// do lots of calculations

// now check for exceptions
if (std::fetestexcept(FE_INVALID))
    throw std::domain_error("argh");
else if (std::fetestexcept(FE_OVERFLOW))
    throw std::overflow_error("eek");
// ...

The exception flags are "sticky" and no other operation, besides an explicit feclearexcept(), should clear them.

The main benefits of using feenablexcept() to force a SIGFPE are that you would detect the error immediately, without waiting until you happen to get around to testing the exception flag, and that you could pinpoint the exact instruction that caused the exception. It doesn't sound like either of those are needed in your use case.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82