double exp(double)意外返回NaN(没有浮点溢出)

时间:2014-01-21 20:47:12

标签: c++ double overflow nan exp

根据普朗克定律,我一直致力于实施黑体辐射:

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给出的波长(波长) = array-index + min)。 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);
  }
}

问题在于,line谱.BlackBody()将数组的第0个元素设置为NaN,而只将第0个元素设置为NaN。此外,第一次迭代也不会发生,但是xc&gt; = 10以下的所有迭代都会发生。

VS调试器中的文本: spectrum = {intensity = 0x009bec50 {-1。#IND0000,520718784。,537559104.,554832896。,572547904。,590712128.,609333504。,...}}

我跟踪了错误,并且:: BlackBody()函数中的exppart变为NaN,基本上exp()返回NaN,即使它的参数接近2.0,所以definetely不会溢出。但仅适用于数组索引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);

2 个答案:

答案 0 :(得分:1)

如果您碰巧使用MSVC 2013,可能的一种解释是您在某处尝试将浮点无穷大转换为int。当发生这种情况时,MSVC 2013中的错误会导致x87 FPU堆栈上的不平衡推送。触发该错误8次并且您的FPU堆栈完全填满,并且任何后续尝试推送值(例如调用'exp()')将导致'无效操作'并返回无限期(如1.#IND) 。请注意,即使您使用SSE2浮点指令进行编译,此错误仍然可以咬人,因为调用约定规定在FPU堆栈顶部返回浮点返回值。

要检查这是否是您的问题,请在错误调用'exp()'之前查看您的FPU寄存器。如果您的TAGS寄存器全为零,那么您的FPU堆栈已满。

MS声称这将在MSVC 2013的更新2中修复。

答案 1 :(得分:-1)

以下函数调用只有1个参数:

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

因此无法调用您定义为

的函数
double BlackBody(double T, double wavelength)

如果你看一下::BlackBody实现,我打赌你在某个地方有0分之差。