SIGFPE Shenanigans

Я повторяю один и тот же расчет дважды, но в одном я получаю исключение с плавающей запятой, а в другом — нет.

#include <iostream>
#include <cmath>
#include <fenv.h>

using namespace std;

int main(void)
{
feenableexcept(-1);

double x,y,z;

x = 1.0;

y = (1.0/(24.3*24.0*3600.0))*x;
cout << "y = " << y << endl;

z = x/(24.3*24.0*3600.0);
cout << "z = " << z << endl;

return 0;
}

Я проверил его на g ++ и clang ++ и получил следующий вывод в обоих

y = 4.76299e-07
Floating point exception

В чем дело?

4

Решение

Проблема с

feenableexcept(-1);

Это наборы FPE исключения для всех возможных случаев включите неточные результаты (которые очень распространены в операциях с плавающей запятой). Вы действительно не должны использовать число здесь, вместо этого используйте предоставленные макросы, чтобы установить нужные биты.

Замена на

feenableexcept(FE_INVALID   |
FE_DIVBYZERO |
FE_OVERFLOW  |
FE_UNDERFLOW);

решает проблему для меня.

когда

feenableexcept(FE_INVALID   |
FE_DIVBYZERO |
FE_OVERFLOW  |
FE_UNDERFLOW |
FE_INEXACT);

дается, SIGFPE вернется. Это показывает, что FE_INEXACT является основной причиной проблемы.

Причина, по которой первый расчет не дает SIGFPE, заключается в том, что деление уже выполнено во время компиляции (с неточными результатами). Во время выполнения выполняется только умножение, что не вносит дополнительной неточности.

4

Другие решения

Это исключение FE_INEXACT.
Это значит x умножается на константу 1/(24.3*24.0*3600.0) вычисленные во время компиляции не могут быть преобразованы в удвоение без потери точности.

Первая операция не вызывает это исключение, потому что x равен 1.0, что имеет точное представление, и константа уже была преобразована в некоторое (неточное) двойное представление во время компиляции.

Поскольку обработка исключений с плавающей запятой не стандартизирована, это может остаться незамеченным на других компиляторах / платформах.

#include <iostream>
#include <cmath>
#include <fenv.h>

using namespace std;

int main(void)
{
feenableexcept(FE_INEXACT); // comment this line out and the exception is gone

double x,y,z;

x = 1.0;

y = (1.0/(24.3*24.0*3600.0))*x;
cout << "y = " << y << endl;
z = x/(24.3*24.0*3600.0);      // <-- FE_INEXACT exception
cout << "z = " << z << endl;

return 0;
}

Это исключение, очевидно, отключено по умолчанию, иначе вы вряд ли сможете выполнить какие-либо вычисления с плавающей запятой вообще.

5

По вопросам рекламы [email protected]