В моем коде C или C++ я хочу, чтобы деление на 0 приводило к значению с плавающей запятой +Infinity или -Infinity, вызывая sqrt(-1) или log(0) для получения значения с плавающей запятой NaN и т. д. в соответствии со спецификацией IEE754. Текущее поведение, которое я наблюдаю, когда происходит что-либо из этого, представляет собой исключение с плавающей запятой. Перехват исключений выходит за рамки моей программы, поскольку она выполняет большое количество операций, а затем выполняет агрегатную функцию, поэтому, если даже один NaN или бесконечность, она должна «отравить» результаты и сделать его бесконечностью или бесконечностью. не-число.
Я упоминаю C и C++, потому что существует некоторая совместимость, например. функция C, скомпилированная в общую библиотеку объектов, которая может выполнять операцию с плавающей запятой, но вызывается из C++.
Первоначально я смог написать некоторый код на C, и казалось, что по умолчанию НЕ выбрасывалось исключение. Однако это был одноразовый код для проверки поведения, и, к сожалению, я его не сохранил. Теперь, когда я пытаюсь это сделать, я не могу вернуться к тому моменту, когда я могу поместить NaN в число с плавающей запятой, а затем распечатать его, вместо этого я получаю исключение.
В С++ я использую:
#include <cfenv>
затем в моем коде, прежде чем я вызову функцию, которая может генерировать бесконечность или NaN, я делаю это:
std::fenv_t envp;
// Clear all floating-point exceptions (not strictly necessary?)
std::feclearexcept(FE_ALL_EXCEPT);
// save floating point environment and then don't generate exceptions
std::feholdexcept(&envp);
// Disable all floating-point exceptions (just for good measure)
fedisableexcept(FE_ALL_EXCEPT);
Я компилирую это с помощью g++ со следующими флагами:
-fno-trapping-math -fno-signaling-nans
В C я использую аналогичный подход:
#include <fenv.h>
#pragma STDC FENV_ACCESS ON
int main() {
fenv_t envp;
printf("debug0:%d\n",feholdexcept(&envp));
printf("debug1:%f\n",1/0);
}
примечание: я попробовал прагму в своем коде на C++, но g++ не распознал ее.
gcc -fno-trapping-math -fno-signaling-nans -o test test.c -lm
Версия gcc — 6.3, версия g++ — 6.5 (на данный момент у меня нет возможности использовать более новую версию, поскольку они работают в производственных системах.)
Деление IIRC на ноль не вызывает исключений. Вместо этого у вас, вероятно, произошел сбой в работе системы (SIGFPE).
@Fareanor - это то, что я имел в виду, вам придется перехватить прерывание, на MSVC код обычно зависает.
@WeatherVane: Re: «C не генерирует исключений (за исключением, возможно, целочисленного деления на ноль)»: исключения C++ и исключения с плавающей запятой — это разные вещи. Стандарт C допускает исключения с плавающей запятой и позволяет их перехватывать.
1/0
не является плавающей запятой. Это целочисленная арифметика, и она не может производить бесконечность... Вы имели в виду 1.0/0
?
@ChrisMM хороший улов... код библиотеки немного сложнее, но я думаю, что приведение к плавающему состоянию может отсутствовать в каком-то месте...
Вам следует перефразировать свой вопрос. В плавающей запятой исключением является «Событие, которое происходит, когда операция над некоторыми конкретными операндами не дает результата, подходящего для любого разумного приложения» (IEEE-754 2019, пункт 2.1). Например, деление 1 на 3 создает неточное исключение. Событие здесь в том, что возникает это условие. Это не означает, что используется ловушка. Вы ничего не можете сделать, чтобы предотвратить исключение; это особенность операции. Кажется, вы хотите предотвратить ловушки; вы хотите, чтобы выполнение продолжалось без остановки или перехвата.
Деление целого числа на ноль может привести к SIGFPE, по крайней мере, в Linux, но это выходит за рамки языковых стандартов. Вы не можете подавить этот SIGFPE с помощью функций из <fenv>
, и вы не можете вместо этого получить inf
.
Выражение 1/0
вызывает исключение, поскольку вы выполняете деление на ноль целых чисел. Здесь не используется тип с плавающей запятой.
Там, где в вашем реальном коде происходит деление на ноль, необходимо использовать типы с плавающей запятой, чтобы результат генерировал значение INF.
ах, действительно... это была всего лишь проверка, но похоже, что в реальных вычислениях используется структура данных, в которой есть целое число, которое нужно преобразовать в число с плавающей запятой перед выполнением вычислений.
C не генерирует исключений (за исключением, возможно, целочисленного деления на ноль).