NaN с плавающей точкой в ​​зависимости от некоррелированной обработки исключений в C++

Очень странно:

double *data; // uncorrelated
double a,b,c;
double sigma = 1e-309; // denormalized number

try { data = new double[10]; } // uncorrelated
catch(...) { cout << "error"; return 1; }

a = 1/sigma;                    // infinite
b = exp(-1/sigma);              // 0
c = a * b;                      // NaN
cout << c << endl;
c = (1/sigma) * exp(-1/sigma);  // 0
cout << c << endl;

Хорошо, второй результат c может быть 0 из-за некоторой оптимизации.

НО: когда я удаляю блок try/catch, второй c остается NaN! Почему это другое поведение??? Мой компилятор VC++ 2010 Express. ОС Windows 7 64-битная. Я использую только стандартные библиотеки, такие как iostream и cmath.

Изменить: мое первое наблюдение было с настройками Debug+Win32 по умолчанию для пустого консольного приложения. С Release+Win32 результаты: первый c 0, второй c NaN - независимо от того, присутствует ли try/catch или нет! Резюме:

                                 //Debug+Win32              // Release+Win32
                              //with try   //without     //with try   //without
c = a * b;                     // NaN         NaN             0           0
c = (1/sigma) * exp(-1/sigma); // 0           NaN            NaN         NaN

Редактировать 2: Когда я устанавливаю /fp:strict переключиться в C++/ генерацию кода, результат такой же с Debug+Win32, но с Release+Win32 он меняется на c = a * b; // NaN а также c = (1/sigma) * exp(-1/sigma); // 0 неважно, если попытаться или нет. Я не понимаю, почему это остается NaN+NaN с Debug+Win32 и без предыдущей попытки. Как отладить программу, которая должна быть безопасной с плавающей точкой, когда результаты отличаются независимо /fp:strict от релиза в зависимости от предыдущей попытки?

Редактировать 3: Здесь полная программа:

// On VC++ 2010 Express in default Win32-Debug mode for empty console application.
// OS: Windows 7 Pro 64-Bit, CPU: Intel Core i5.
// Even when /fp:strict is set, same behaviour.
//
// Win32-Release mode: first c == 0, second c == NaN (independent of try)
// with /fp:strict: first c == NaN, second c == 0 (also independent of try)

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
    double *data; // uncorrelated
    double a,b,c;
    double sigma = 1e-309; // denormalized number

    try { data = new double[10]; } // uncorrelated
    catch(...) { cout << "error"; return 1; }

    a = 1/sigma;                    // infinite
    b = exp(-1/sigma);              // 0
    c = a * b;                      // NaN
    cout << c << endl;
    c = (1/sigma) * exp(-1/sigma);  // 0 with preceding try or
    cout << c << endl;              // NaN without preceding try

    cin.get();
    return 0;
}

1 ответ

Решение

Подобные вещи могут происходить из-за различий в распределении / использовании регистров. Например - с блоком try-catch значение sigma может быть сохранен как 64-битный double затем загружается из памяти, в то время как без блока он может использовать 80-битный регистр с более высокой точностью (см. http://en.wikipedia.org/wiki/Extended_precision) без округления до 64 бит. Я предлагаю вам проверить вашу сборку, если вам не все равно.

Другие вопросы по тегам