У меня есть кусок кода, вычисляющий вектор функций вектора независимых переменных (и параметров):
struct instance
{ double m_adParam1, m_adParam2;
std::array<double, OUTSIZE> calculate(const std::array<double, INSIZE>&x) const;
};
Когда какая-либо часть этого кода дает сбой из-за деления на (почти) ноль или из-за ошибки домена, весь код должен дать сбой и сообщить вызывающей стороне, что он не может вычислить эту функцию для данного ввода.
Это также должно применяться к расчетным значениям, используемым только для сравнения. Поэтому проверки результирующего вектора на NAN или бесконечность недостаточно. Вставка тестов после каждой ошибочной операции невозможна.
Как можно это сделать?
Я читал о festestexcept(). Но непонятно, что сбрасывает этот государственный флаг. SIGFPE бесполезен ни для чего, кроме отладки. Было бы здорово иметь возможность преобразовать это в исключения C++, как в Windows, но также было бы достаточно вставить окончательную проверку.
Вставка кода для проверки недопустимых входных данных для следующего кода также невозможна, так как это значительно замедлит вычисления.
SIGFPE
не исключение. Это часть ОС. Исключения C++ являются частью языка.
Несмотря на то, что они имеют одно и то же имя, исключения ОС и исключения C++ — это две совершенно разные вещи.
Вы можете создать свой собственный класс safe_double
, который обертывает double
и будет вызывать исключение, если в операции выполняется условие ошибки. Тем не менее, это делает каждую из операций проверенной, что может быть даже хуже, чем проверка правильности домена ввода.
@ 273K: SIGFPE
— это сигнал ОС, доставляемый в ответ на аппаратное исключение, если вы демаскируете исключения с плавающей запятой. По умолчанию все исключения FP маскируются, поэтому они просто записываются в MXCSR (что и проверяет fetestexcept
). В любом случае, если ваш код демаскирует исключения FP, а затем запускает одно из них, вы получаете SIGFPE
. Нет ничего неправильного в том, чтобы назвать это исключением, и этот вопрос действительно спрашивает о преобразовании его в исключение C++, правильно проводя различие между исключениями HW FP и исключениями C++. (IDK, насколько возможно было бы написать обработчик SIGFPE, который выбрасывает родительский поток)
Предполагая, что вы используете glibc, делает ли feenableexcept( FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
то, что вы хотите?
@Frank Puck Учитывая, что «когда какая-либо часть этого кода дает сбой из-за деления на (почти) ноль ... весь код должен дать сбой», почему деление почти на ноль (а не на ноль) должно завершиться ошибкой? Что не так с коэффициентом +/- бесконечности?
Что касается выбрасывания изнутри обработчика сигнала, см. stackoverflow.com/questions/1717991/…. Есть примечание, что MSVC++ поддерживает его, и что gcc с -fnon-calll-exceptions
может работать на некоторых платформах. Я не проверял.
В C наиболее переносимым подходом является setjmp/longjmp
, но он плохо взаимодействует с C++, так как не раскручивает стек; если вы longjmp
выходите за рамки некоторых объектов, их деструкторы вызываться не будут. Это может сработать, если вы держите setjmp
достаточно близко к коду расчета, чтобы между ними не создавались (нетривиально) никакие объекты, и тогда вы могли бы throw
, когда setjmp возвращается во второй раз.
Я читал о festestexcept(). Но непонятно, что сбрасывает этот государственный флаг.
Это то, что делает feclearexcept(). Итак, вы можете сделать что-то вроде
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");
// ...
Флаги исключений являются «липкими», и никакая другая операция, кроме явного feclearexcept()
, не должна их очищать.
Основные преимущества использования feenablexcept()
для форсирования SIGFPE заключаются в том, что вы сразу обнаружите ошибку, не дожидаясь, пока вам не удастся проверить флаг исключения, и что вы сможете точно определить инструкцию, вызвавшую исключение. Похоже, что ни один из них не нужен в вашем случае использования.
Разве вы не должны проверять перед любой из этих операций, удовлетворяются ли ограничения домена и в конечном итоге сигнализировать об ошибке?