Я работал над реализацией излучения черного тела в соответствии с законом Планка со следующим:
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;
}
У меня есть массив float [max-min + 1], где static const int max = 780, static const int min = 380. Я просто перебираю массив и вставляю то, что BlackBody дает для длины волны (wavelength = array- индекс + мин). 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, и только 0-й. Также это не происходит для самой первой итерации, но для всех следующих, где xc> = 10.
Текст из отладчика VS:
спектр = {интенсивность = 0x009bec50 {-1. # IND0000, 520718784., 537559104., 554832896., 572547904., 590712128., 609333504., …}}
Я отследил ошибку, и exppart в функции :: BlackBody () становится NaN, в основном exp () возвращает NaN, даже если его аргумент близок к 2.0, поэтому определенно не переполнен. Но только для индекса массива 0. Он волшебным образом начинает работать для остальных 400 индексов.
Я знаю, что переполнение памяти может вызвать такие вещи. Вот почему я дважды проверил свою обработку памяти.
Я связываю Vec3 из другой самодельной библиотеки, которая намного больше и может содержать ошибки, но то, что я использую из Vec3, не имеет ничего общего с памятью.
После многих часов я совершенно невежественен. Что еще может вызвать это? Меня обманывает оптимизатор или WINAPI …? (Хм, да, программа создает окно с 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);
Если вы используете MSVC 2013, одним из возможных объяснений является то, что где-то есть код, который пытается преобразовать бесконечность с плавающей точкой в int. Ошибка в MSVC 2013 вызывает несбалансированный толчок в стеке x87 FPU, когда это происходит. Инициируйте эту ошибку 8 раз, и ваш стек FPU будет полностью заполнен, и любая последующая попытка выдвинуть значение (например, вызов exp ()) приведет к «недопустимой операции» и возврату неопределенного значения (например, 1. # IND) , Обратите внимание, что даже если вы компилируете инструкции SSE2 с плавающей запятой, эта ошибка может все еще кусаться, потому что соглашение о вызовах диктует, что возвращаемые значения с плавающей запятой возвращаются в верхней части стека FPU.
Чтобы проверить, является ли это вашей проблемой, взгляните на свои регистры FPU непосредственно перед неправильным вызовом exp (). Если ваш регистр TAGS равен нулю, то ваш стек FPU заполнен.
MS утверждает, что это будет исправлено в обновлении 2 для MSVC 2013.
Следующий вызов функции имеет только 1 параметр:
spectrum.BlackBody(200.f + xc * 200.f);
Так что это не может быть вызов функции, которую вы определили как
double BlackBody(double T, double wavelength)
Если вы посмотрите на ::BlackBody
реализация, держу пари, у вас есть ошибка деления на 0 где-то.