Очень странно:
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;
}
Подобные вещи могут происходить из-за различий в распределении / использовании регистров. Например — с блоком try-catch значение sigma
может быть сохранен как 64-битный double
затем загружается из памяти, в то время как без блока может использоваться 80-битный регистр с более высокой точностью (см. http://en.wikipedia.org/wiki/Extended_precision) без округления до 64 бит. Я предлагаю вам проверить свою сборку, если вам не все равно.
Других решений пока нет …