Я использую C++ в экспрессе Visual Studio для генерации случайных деревьев выражений для использования в типе генетического алгоритма программы.
Поскольку они случайны, деревья часто генерируют: разделитесь на нуль, переполнение, потерю значимости, а также возвращающийся "inf" и другие строки. Я могу записать обработчики для строк, но литература оставила меня экранированным о других. Если я понимаю это правильно, я должен установить некоторые флаги сначала?
Совет и/или указатель на некоторую литературу ценились бы.Править: значения, возвращенные в двойной переменной, 1.#INF или-1.#IND. Я был неправ назвать их строками.
Вы уверены, что хотите поймать их, а не просто игнорировать? Предполагая, что вы хотите просто игнорировать их:
См. это: http://msdn.microsoft.com/en-us/library/c9676k6h.aspx
Для маски _MCW_EM очистка маски устанавливает исключение, что позволяет аппаратное исключение; установка маски скрывает исключение.
Поэтому вы захотите сделать что-то вроде этого:
#include <float.h>
#pragma fenv_access (on)
void main()
{
unsigned int fp_control_word;
unsigned int new_fp_control_word;
_controlfp_s(&fp_control_word, 0, 0);
// Make the new fp env same as the old one,
// except for the changes we're going to make
new_fp_control_word = fp_control_word | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT;
//Update the control word with our changes
_controlfp_s(&fp_control_word, new_fp_control_word, _MCW_EM)
}
Некоторая путаница здесь может быть связана с использованием слова "исключение". В C++ это обычно относится к встроенной в язык системе обработки исключений. Исключения с плавающей запятой - это совсем другой зверь. Все исключения, которые должен поддерживать стандартный FPU, определены в стандарте IEEE-754. Они происходят внутри блока с плавающей точкой, который может делать разные вещи в зависимости от того, как настроены управляющие флаги блока с плавающей точкой. Обычно происходит одно из двух: 1) Исключение игнорируется, и FPU устанавливает флаг, указывающий на возникновение ошибки, в своем регистре (регистрах) состояния. 2) Исключение не игнорируется FPU, поэтому вместо него генерируется прерывание, и вызывается обработчик прерывания, который был установлен для ошибок с плавающей запятой. Обычно это делает что-нибудь приятное для вас, например, заставляет вас прерваться на этой строке кода в отладчике или генерирует файл ядра.
Подробнее о IEE-754 вы можете узнать здесь: http://www.openwatcom.org/ftp/devel/docs/ieee-754.pdf
Некоторые дополнительные ссылки по плавающей точке: http://docs.sun.com/source/806-3568/ncg_goldberg.html http://floating-point-gui.de/
Никогда не пробовал, но если предположить, что деление на ноль вызывает исключение, вы можете просто заключить код в команду try / catch следующим образом:
try { // потенциальное деление на ноль ... } catch (...) { // ... перехватывает ВСЕ исключения }
{ {1}}Среда выполнения C++ не окажет вам здесь ни малейшей помощи. Вам придется выполнять эти проверки самостоятельно, явно в своем коде. Если, конечно, функции, которые вы вызываете, не выполняют эти проверки - в этом случае все зависит от того, как они поведут себя в случае ошибки.
Позвольте мне объяснить:
double divide(double a, double b) {
return a / b; // undefined if b is zero
}
Должно быть на самом деле
double divide(double a, double b) {
if( b == 0 ) {
// throw, return, flag, ... you choose how to signal the error
}
return a / b; // b can't possibly be zero here
}
Если код, который не работает при делении на ноль и тому подобном, не ваш, то вам придется копнуть глубже, чтобы найти, что он делает в случае угрозы ошибки. Выбрасывает ли он? Устанавливает флаг? Спросите автора и/или прочитайте источник.
Вот пример для исключения:
struct bad_value : public std::exception { };
double divide(double a, double b) {
if( b == 0 ) throw bad_value("Division by zero in divide()");
return a / b; // b can't possibly be zero here
}
// elsewhere (possibly in a parallel universe) ...
try {
double r1 = divide(5,4);
double r2 = divide(5,0);
} catch( bad_value e ) {
// ...
}