плавающее исключение с использованием компилятора icc - PullRequest
0 голосов
/ 02 мая 2010

Я компилирую свой код с помощью следующей команды:

icc -ltbb test.cxx -o test

Затем, когда я запускаю программу:

time ./mp6 100 > output.modified
Floating exception
4.871u 0.405s 0:05.28 99.8%     0+0k 0+0io 0pf+0w

Я получаю «Плавающее исключение». Это следующий код на C ++, который был у меня до исключения и после:

// before
if (j < E[i]) {
   temp += foo(0, trr[i], ex[i+j*N]);
}

// after
temp += (j < E[i])*foo(0, trr[i], ex[i+j*N]);

Это булева алгебра ... так что (j

int foo(int s, int t, int e) {
    switch(s % 4) {
        case 0:
            return abs(t - e)/e;
        case 1:
            return (t == e) ? 0 : 1;
        case 2:
            return (t < e) ? 5 : (t - e)/t;
        case 3:
            return abs(t - e)/t;
    }
    return 0;
}

foo () не является функцией, которую я написал, поэтому я не слишком уверен в том, что она делает ... но я не думаю, что проблема в функции foo (). Есть ли что-то в булевой алгебре, которую я не понимаю, или что-то, что работает в С ++ иначе, чем я знаю? Любые идеи, почему это вызывает исключение?

Спасибо, Христо

Ответы [ 4 ]

3 голосов
/ 02 мая 2010

Вы почти наверняка делитесь на ноль в foo.

Простая программа

int main()
{
    int bad = 0;
    return 25/bad;
}

также печатает

Floating point exception

в моей системе.

Итак, вы должны проверить, равен ли e 0, если s % 4 равен нулю, или t равен 0, когда s % 4 равен 2 или 3. Затем верните любое значение, которое имеет смысл для вашей ситуации, вместо того, чтобы пытаться делить на ноль.


@hristo: C ++ по-прежнему будет оценивать правую часть умножения, даже если левая часть равна нулю. Неважно, что результат должен быть ноль; важно, что foo был вызван и оценен, что вызвало ошибку.

Источник выборки:

#include <iostream>
int maybe_cause_exception(bool cause_it)
{
    int divisor = cause_it ? 0 : 10;
    return 10 / divisor;
}

int main()
{
    std::cout << "Do not raise exception: " << maybe_cause_exception(false) << std::endl;

    int x = 0;

    std::cout << "Before 'if' statement..." << std::endl;

    if(x)
    {
        std::cout << "Inside if: " << maybe_cause_exception(true) << std::endl;
    }

    std::cout << "Past 'if' statement." << std::endl;

    std::cout << "Cause exception: " << x * maybe_cause_exception(true) << std::endl;

    return 0;
}

Выход:

Do not raise exception: 1

Before 'if' statement...

Past 'if' statement.

1042 * *
Floating point exception
1 голос
/ 02 мая 2010

Хотя я не сообщаю вам точную причину вашего исключения с плавающей точкой, я могу предоставить некоторую информацию, которая может оказаться полезной при расследовании будущих ошибок с плавающей точкой. Я считаю, Марк уже пролил свет на то, почему у вас возникла именно эта проблема.


Наиболее переносимый способ определить, возникло ли исключение с плавающей запятой и его причина - использовать средства исключения с плавающей запятой, предоставляемые C99 в fenv.h . В fenv.h определены 11 функций для управления средой с плавающей запятой (см. Справочную страницу fenv (3) ). Вы также можете найти эту статью интересной.


В POSIX-совместимых системах SIGFPE отправляется процессу, когда in выполняет ошибочную арифметическую операцию, и это не обязательно включает в себя арифметику с плавающей запятой. Если обрабатывается сигнал SIGFPE и в sa_flags для вызова sigaction(2) указан SA_SIGINFO, элемент si_code структуры siginfo_t должен указать причину для неисправность.

Из Википедии SIGFPE Статья :

Общим упущением является рассмотрение деления на ноль единственным источником условий SIGFPE. На некоторых архитектурах (в том числе IA-32 [требуется цитирование] ) целочисленное деление INT_MIN, наименьшего представимого отрицательного целочисленного значения, на -1 запускает сигнал, поскольку частное, положительное число, не представимо.

1 голос
/ 02 мая 2010

Возможно, вы делите на 0?Возможно, целочисленное деление на 0 отображается как «плавающее исключение».

Если у вас есть if, вычисление не будет выполнено, если произойдет деление на 0.Когда вы выполняете «булеву алгебру», вычисления выполняются независимо, что приводит к ошибке деления на 0.

Вы думаете, что это будет temp += 0*foo(...);, поэтому не нужно вызывать fooпотому что 0 раз что-нибудь всегда будет 0), но это не так, как работает компилятор.Обе стороны * должны быть оценены.

0 голосов
/ 02 мая 2010

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

Вы все еще можете использовать трюк умножения, если вы немного трансформируете знаменатель. вместо x/t используйте x/(t + !t), который ни на что не влияет, если знаменатель ненулевой (тогда вы добавляете ноль), но позволяет вычислять знаменатель t = 0, а затем отбрасывать, умножая на ноль.

И извините, но будьте осторожны с моими предложениями, я не знаю всех деталей вашей программы. Кроме того, я склонен сходить с ума по поводу замены веток на «умные» логические выражения

...