Работа с исключениями с плавающей точкой - PullRequest
11 голосов
/ 08 февраля 2010

Я не уверен, что делать с исключениями с плавающей запятой в C или C ++. Из вики есть следующие типы исключений с плавающей точкой:

IEEE 754 specifies five arithmetic errors that are to be recorded in "sticky bits" (by default; note that trapping and other alternatives are optional and, if provided, non-default).  

* inexact, set if the rounded (and returned) value is different from the mathematically exact result of the operation.  
* underflow, set if the rounded value is tiny (as specified in IEEE 754) and inexact (or maybe limited to if it has denormalisation loss, as per the 1984 version of IEEE 754), returning a subnormal value (including the zeroes).  
* overflow, set if the absolute value of the rounded value is too large to be represented (an infinity or maximal finite value is returned, depending on which rounding is used).  
* divide-by-zero, set if the result is infinite given finite operands (returning an infinity, either +∞ or −∞).  
* invalid, set if a real-valued result cannot be returned (like for sqrt(−1), or 0/0), returning a quiet NaN.

Возможно ли, что когда произойдет какой-либо из вышеперечисленных исключений, программа выйдет ненормально? Или программа выдаст эту ошибку, не упоминая ничего, и, следовательно, затруднит ее отладку?

Может ли такой компилятор, как gcc, выдавать предупреждение для некоторого очевидного случая?

Что я могу сделать во время кодирования моей программы, чтобы уведомить, где происходит ошибка и какие типы она возникает, когда она возникает, чтобы я мог легко найти ошибку в своем коде? Пожалуйста, дайте решения как в случае C, так и C ++.

Спасибо и всего наилучшего!

Ответы [ 6 ]

12 голосов
/ 08 февраля 2010

Есть много вариантов, но общая и также философия по умолчанию, введенная 754, заключается в том, чтобы не ловушка, а вместо этого производить специальные результаты, такие как бесконечности, которые могут или не могут показать в важные результаты.

В результате функции, проверяющие состояние отдельных операций, используются не так часто, как функции, проверяющие представление результатов.

См., Например ...

LIST OF FUNCTIONS

 Each of the functions that use floating-point values are provided in sin-
 gle, double, and extended precision; the double precision prototypes are
 listed here.  The man pages for the individual functions provide more
 details on their use, special cases, and prototypes for their single and
 extended precision versions.

 int fpclassify(double)
 int isfinite(double)
 int isinf(double)
 int isnan(double)
 int isnormal(double)
 int signbit(double)

Обновление: Для тех, кто действительно думает, что FPU генерирует SIGFPE в случае по умолчанию, я бы посоветовал вам попробовать эту программу. Вы можете легко генерировать переполнение, переполнение и деление на ноль. То, что вы не сгенерируете (если вы не запустите его на последнем выживающем VAX или не-754 RISC), это SIGFPE:

#include <stdio.h>
#include <stdlib.h>
int main(int ac, char **av) { return printf("%f\n", atof(av[1]) / atof(av[2])); }
6 голосов
/ 08 февраля 2010

В Linux вы можете использовать расширение GNU feenableexcept (скрытое справа внизу этой страницы), чтобы включить перехват исключений с плавающей запятой - если вы сделаете это, вы получите сигнал SIGFPE возникает исключение, которое вы можете затем перехватить в своем отладчике. Однако будьте осторожны, так как иногда сигнал подается на инструкцию с плавающей запятой после , которая на самом деле вызывает проблему, давая неверную информацию о строке в отладчике!

4 голосов
/ 08 февраля 2010

В Windows с Visual C ++ вы можете контролировать, какие исключения с плавающей запятой не маскируются, используя _control87() и т. Д. . Немаскированные исключения с плавающей точкой генерируют структурированные исключения, которые могут быть обработаны с использованием __try / __except (и нескольких других механизмов). Это все полностью зависит от платформы.

Если вы оставляете исключения с плавающей запятой замаскированными, другой зависящий от платформы подход к обнаружению этих условий - очистить состояние с плавающей запятой, используя _clear87() и т. Д. , выполнить вычисления и затем запросить плавающую состояние точки с помощью _status87() и т. д. .

Что-нибудь из этого лучше, чем предложение DigitalRoss проверить результат? В большинстве случаев это не так. Если вам нужно обнаружить (или контролировать) округление (что маловероятно), то, возможно,?

В Windows с Borland / CodeGear / Embarcadero C ++ некоторые исключения с плавающей запятой по умолчанию не маскируются, что часто вызывает проблемы при использовании сторонних библиотек, которые не тестировались с исключениями с плавающей запятой без маскировки.

2 голосов
/ 08 февраля 2010

Различные компиляторы обрабатывают эти ошибки по-разному.

Неточность почти всегда является результатом деления чисел с абсолютным значением больше единицы (возможно, через трансцендентные функции). Сложение, вычитание и умножение чисел с абсолютным значением> 1,0 может привести только к переполнению.

Недостаток происходит не очень часто и, вероятно, не будет проблемой в обычных вычислениях, за исключением повторяющихся функций, таких как ряды Тейлора.

Переполнение - это проблема, которая обычно может быть обнаружена с помощью некоего «бесконечного» сравнения, разные компиляторы различаются.

Деление на ноль весьма заметно, поскольку ваша программа будет (должна) аварийно завершать работу, если у вас нет обработчика ошибок. Проверка дивидендов и делителей поможет избежать проблемы.

Неверные ответы обычно перехватываются без специальных обработчиков ошибок с напечатанной ошибкой DOMAIN.

[EDIT]

Это может помочь: (Руководство по численным вычислениям от Sun) http://docs.sun.com/source/806-3568/

1 голос
/ 08 февраля 2010

C99 представил функции для обработки исключений с плавающей запятой. Перед операцией с плавающей запятой вы можете использовать feclearexcept() для сброса любых выдающихся исключений. После выполнения операции можно использовать fetestexcept(), чтобы проверить, какие флаги исключений установлены.

0 голосов
/ 08 февраля 2010

В Linux вы можете перехватить эти исключения, перехватывая сигнал SIGFPE. Если вы ничего не сделаете, эти исключения прекратят работу вашей программы. Чтобы установить обработчик, используйте функцию сигнала, передавая сигнал, который вы хотите поймать, и функцию, вызываемую в случае срабатывания сигнала.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...