2014-01-21 1 views
0

Я работал над реализацией излучения черного тела в соответствии с законом Планка с нижеследующим:двойной ехр (двойной) неожиданно возвращается NaN (нет переполнения с плавающей точкой)

double BlackBody(double T, double wavelength) { 
    wavelength /= 1e9; // pre-scale wavelength to meters 

    static const double h = 6.62606957e-34; // Planck constant 
    static const double c = 299792458.0; // speed of light in vacuum 
    static const double k = 1.3806488e-23; // Boltzmann constant 

    double exparg = h*c/(k*wavelength*T); 
    double exppart = std::exp(exparg) - 1.0; 
    double constpart = (2.0*h*c*c); 
    double powpart = pow(wavelength, -5.0); 
    double v = constpart * powpart/exppart; 
    return v; 
} 

У меня есть поплавок [максиминимального +1], где static const int max = 780, static const int min = 380. Я просто перебираю массив и добавляю то, что BlackBody дает для длины волны (длина волны = индекс массива + мин). IntensitySpectrum :: BlackBody выполняет эту итерацию, в то время как min и max являются статическими членскими варами, а массив также находится внутри IntensitySpectrum.

IntensitySpectrum spectrum; 

Vec3 rgb = spectrum.ToRGB(); 
rgb /= std::max(rgb.x, std::max(rgb.y, rgb.z)); 
for (int xc = 0; xc < grapher.GetWidth(); xc++) { 
    if (xc % 10 == 0) { 
    spectrum.BlackBody(200.f + xc * 200.f); 
    spectrum.Scale(1.0f/1e+14f); 
    rgb = spectrum.ToRGB(); 
    rgb /= std::max(rgb.x, std::max(rgb.y, rgb.z)); 
    } 
    for (int yc = 20; yc < 40; yc++) { 
    grapher(xc, yc) = grapher.FloatToUint(rgb.x, rgb.y, rgb.z); 
    } 
} 

Проблема заключается в том, что линия spectrum.BlackBody() устанавливает 0-й элемент массива к NaN, и только 0th. Также это не происходит для самой первой итерации, но все следующие, где xc> = 10.

текст из VS отладчик: {. Интенсивность = 0x009bec50 {-1 # IND0000, 520718784., 537559104., 554832896., 572547904., 590712128., 609333504., ...}} спектра =

Я отслеживал ошибку вниз, а функция part :: blackBody() становится NaN, в основном exp() возвращает NaN, хотя аргумент равен около 2.0, поэтому определенно не переполняется. Но только для индекса массива 0. Он волшебным образом начинает работать для остальных 400 индексов.

Я знаю, что перерасход памяти может привести к подобным вещам. Вот почему я дважды проверил работу с памятью. Я связываю Vec3 с другой самодельной библиотекой, которая намного больше и может содержать ошибки, но то, что я использую из Vec3, не имеет ничего общего с памятью.

После многих часов я совершенно не знаю. Что еще может вызвать это? Оптимизатор или WINAPI обманывают меня ...? (Uhm, да, программа создает окно с WINAPI и использует почти пустой WndProc, который вызывает мой код на WM_PAINT.)

Спасибо за помощь.

Извините, что вы не поняли. Это расположение:

// member 
class IntensitySpectrum { 
public: 
    void BlackBody(float temperature) { 
     // ... 
     this->intensity[i] = ::BlackBody(temperature, wavelength(i)); 
     // ... 
    } 
private: 
    static const int min = 380; 
    static const int max = 780; 
    float intensity[max-min+1]; 
} 

// global 
double BlackBody(double T, double wavelength); 
+4

Вы показали 'двойной чернотельный (двойной T, двойной длины волны)', который, по-видимому, не то, что вы вызывают в другом блоке кода. Там вы вызываете 'IntensitySpectrum :: BlackBody (float)' или 'IntensitySpectrum :: BlackBody (double)'. Второго аргумента нет, и вы не используете возвращаемое значение из этого вызова функции. –

ответ

-1

Следующий вызов функции только имеет один параметр:

spectrum.BlackBody(200.f + xc * 200.f); 

Так что не может быть вызовом функции вы определили, как

double BlackBody(double T, double wavelength) 

Если вы посмотрите на ::BlackBody реализация, я уверен, что у вас есть ошибка с ошибкой 0.

+0

Эти два «BlackBody» - это разные функции, один из которых является членом, который вызывает другой, который является глобальным. Я добавил небольшой фрагмент кода для уточнения. Кроме того, нет деления на 0, это, в частности, exp(), которые возвращают NaN. – petiaccja

+0

@petiaccja Что такое значение 'длина волны', когда вы получаете результат« NaN »? –

+1

Он перемещается от 380,0 (мин) до 780,0 (макс.) В петле. Затем делится на 1e + 9 в BlackBody для получения счетчиков, поэтому для exp() используется 380e-9 to 780e-9. 380 вызывает NaN, а 381 - 780 работает отлично. – petiaccja

1

Если вы используете MSVC 2013, одно из возможных объяснений заключается в том, что у вас есть какой-то код где-то, пытающийся преобразовать бесконечность float в int. Ошибка в MSVC 2013 вызывает неуравновешенное нажатие на столбец FPU x87, когда это происходит. Запуск этой ошибки 8 раз и стека FPU полностью заполнен, и любая последующая попытка нажимать значение (например, вызов «exp()») приведет к «недействительной операции» и возвращает неопределенный (например, 1. # IND) , Обратите внимание, что даже если вы компилируете инструкции SSE2 с плавающей запятой, эта ошибка все равно может укусить, потому что соглашение о вызове диктует, что возвращаемые значения с плавающей запятой возвращаются в верхней части стека FPU.

Чтобы проверить, не является ли это вашей проблемой, просмотрите свои регистры FPU непосредственно перед плохим вызовом на «exp()». Если ваш регистр TAGS равен нулю, ваш стек FPU будет заполнен.

MS утверждает, что это будет исправлено в обновлении 2 для MSVC 2013.