2

我有一个问题,经过多次挠头,我认为这与长双打中非常小的数字有关。

我正在尝试实现普朗克定律方程,以在给定波长范围和给定温度之间以 1nm 的间隔生成归一化黑体曲线。最终这将是一个接受输入的函数,现在它是 main(),变量固定并由 printf() 输出。

我在matlabpython中看到了示例,它们在类似的循环中实现了与我相同的方程,完全没有问题。

这是等式

普朗克斯定律

我的代码生成了不正确的黑体曲线: 黑体码输出错误

我已经独立测试了代码的关键部分。在尝试通过在 excel 中将方程分解成块来测试方程后,我注意到它确实会产生非常小的数字,我想知道我对大数字的实现是否会导致问题?有没有人对使用 C 来实现方程有任何见解?这对我来说是一个新领域,我发现数学比普通代码更难实现和调试。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>  

//global variables
const double H = 6.626070040e-34; //Planck's constant (Joule-seconds)
const double C = 299800000;       //Speed of light in vacume (meters per second)
const double K = 1.3806488e-23;   //Boltzmann's constant (Joules per Kelvin)
const double nm_to_m = 1e-6;      //conversion between nm and m
const int interval = 1;           //wavelength interval to caculate at (nm)

//typedef structure to hold results
typedef struct {
    int *wavelength;
    long double *radiance;
    long double *normalised;
} results;

int main() {
    int min = 100 , max = 3000;            //wavelength bounds to caculate between, later to be swaped to function inputs
    double temprature = 200;               //temprature in kelvin, later to be swaped to function input
    double new_valu, old_valu = 0;
    static results SPD_data, *SPD;         //setup a static results structure and a pointer to point to it
    SPD = &SPD_data;
    SPD->wavelength = malloc(sizeof(int) * (max - min));          //allocate memory based on wavelength bounds
    SPD->radiance = malloc(sizeof(long double) * (max - min));
    SPD->normalised = malloc(sizeof(long double) * (max - min));

    for (int i = 0; i <= (max - min); i++) {
        //Fill wavelength vector
        SPD->wavelength[i] = min + (interval * i);

        //Computes radiance for every wavelength of blackbody of given temprature
        SPD->radiance[i] = ((2 * H * pow(C, 2)) / (pow((SPD->wavelength[i] / nm_to_m), 5))) * (1 / (exp((H * C) / ((SPD->wavelength[i] / nm_to_m) * K * temprature))-1));

        //Copy SPD->radiance to SPD->normalised
        SPD->normalised[i] = SPD->radiance[i];

        //Find largest value
        if (i <= 0) {
            old_valu = SPD->normalised[0];
        } else if (i > 0){
            new_valu = SPD->normalised[i];
            if (new_valu > old_valu) {
                old_valu = new_valu;
            }
        }
    }
    //for debug perposes
    printf("wavelength(nm)  radiance(Watts per steradian per meter squared)  normalised radiance\n");

    for (int i = 0; i <= (max - min); i++) {
        //Normalise SPD
        SPD->normalised[i] = SPD->normalised[i] / old_valu;

        //for debug perposes
        printf("%d %Le %Lf\n", SPD->wavelength[i], SPD->radiance[i], SPD->normalised[i]);
    }
    return 0; //later to be swaped to 'return SPD';
}

/************************更新 2017 年 3 月 24 日星期五 23:42************************ *****/

到目前为止,感谢您的建议,许多有用的指针特别是了解数字在 C ( IEEE 754 ) 中的存储方式,但我认为这不是这里的问题,因为它仅适用于有效数字。我实施了大部分建议,但在这个问题上仍然没有进展。我怀疑评论中的 Alexander 可能是对的,我可能需要更改操作的单位和顺序才能使方程式像 matlab 或 python 示例一样工作,但我的数学知识不足以做到这一点。我把方程分解成几块,仔细看看它在做什么。

//global variables
const double H = 6.6260700e-34;   //Planck's constant (Joule-seconds) 6.626070040e-34
const double C = 299792458;         //Speed of light in vacume (meters per second)
const double K = 1.3806488e-23;     //Boltzmann's constant (Joules per Kelvin) 1.3806488e-23
const double nm_to_m = 1e-9;        //conversion between nm and m
const int interval = 1;             //wavelength interval to caculate at (nm)
const int min = 100, max = 3000;    //max and min wavelengths to caculate between (nm)
const double temprature = 200;      //temprature (K)

//typedef structure to hold results
typedef struct {
    int *wavelength;
    long double *radiance;
    long double *normalised;
} results;

//main program
int main()
{
    //setup a static results structure and a pointer to point to it
    static results SPD_data, *SPD;
    SPD = &SPD_data;
    //allocate memory based on wavelength bounds
    SPD->wavelength = malloc(sizeof(int) * (max - min));
    SPD->radiance = malloc(sizeof(long double) * (max - min));
    SPD->normalised = malloc(sizeof(long double) * (max - min));

    //break equasion into visible parts for debuging
    long double aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll, mm, nn, oo;

    for (int i = 0; i < (max - min); i++) {
        //Computes radiance at every wavelength interval for blackbody of given temprature
        SPD->wavelength[i] = min + (interval * i);

        aa = 2 * H;
        bb = pow(C, 2);
        cc = aa * bb;
        dd = pow((SPD->wavelength[i] / nm_to_m), 5);
        ee = cc / dd;

        ff = 1;
        gg = H * C;
        hh = SPD->wavelength[i] / nm_to_m;
        ii = K * temprature;
        jj = hh * ii;
        kk = gg / jj;
        ll = exp(kk);
        mm = ll - 1;
        nn = ff / mm;

        oo = ee * nn;

        SPD->radiance[i] = oo;
    }

    //for debug perposes
    printf("wavelength(nm) | radiance(Watts per steradian per meter squared)\n");
    for (int i = 0; i < (max - min); i++) {
        printf("%d %Le\n", SPD->wavelength[i], SPD->radiance[i]);
    }
    return 0;
}

xcode中运行时的方程变量值:

xcode中运行时的变量值

4

2 回答 2

4

我注意到一些关于你的程序当前状态的错误和/或可疑之处:

  • 您已定义nm_to_m为 10 -9,,但您除以它。如果您的波长以纳米为单位测量,则应将其乘以 10 -9以得到以米为单位的波长。也就是说,如果hh应该是你的波长,以米为单位,它是几个光小时的数量级。
  • 显然也是如此dd
  • mm,作为指数表达式负 1,为零,这使您可以得到无穷大的结果。这显然是因为双精度数中没有足够的数字来表示指数的重要部分。不要在exp(...) - 1此处使用,而是尝试使用该expm1()函数,它实现了一个定义明确的算法,用于计算指数负 1,而不会出现取消错误。
  • 因为interval是 1,所以目前没有关系,但是您可能会看到,如果您设置interval为其他值,您的结果将与代码的含义不匹配。
  • 除非您计划在未来对此进行更改,否则该程序不需要“保存”所有计算的值。您可以在运行它们时将它们打印出来。

另一方面,您似乎没有任何下溢或溢出的危险。您使用的最大和最小数字似乎与 10 ±60相差不远,这完全在普通doubles 可以处理的范围内,更不用说long doubles 了。话虽这么说,使用更多的标准化单位可能不会有什么坏处,但是以您当前显示的数量级,我不会担心。

于 2017-03-25T01:03:48.330 回答
3

感谢评论中的所有指示。对于在 C 中实现方程式时遇到类似问题的其他人,我在代码中遇到了一些愚蠢的错误:

  • 写 6 而不是 9
  • 当我应该乘法时除法
  • 我的数组大小与 for() 循环的迭代次数相差一个错误
  • 200 当我的意思是温度变量中的 2000 时

由于最后一个结果,特别是我没有得到我预期的结果(我的波长范围不适合绘制我正在计算的温度),这导致我假设方程的实现有问题,具体来说,我在考虑 C 中的大/小数字,因为我不理解它们。此情况并非如此。

总而言之,在代码中实现之前,我应该确保我确切地知道我的方程在给定的测试条件下应该输出什么。我将努力让数学变得更舒服,尤其是代数维度分析

下面是工作代码,作为一个函数实现,可以随意使用它,但显然没有任何形式的保证等。

黑体.c

//
//  Computes radiance for every wavelength of blackbody of given temprature
//
//  INPUTS: int min wavelength to begin calculation from (nm), int max wavelength to end calculation at (nm), int temperature (kelvin)
//  OUTPUTS: pointer to structure containing:
//              - spectral radiance (Watts per steradian per meter squared per wavelength at 1nm intervals)
//              - normalised radiance
//

//include & define
#include "blackbody.h"

//global variables
const double H = 6.626070040e-34;   //Planck's constant (Joule-seconds) 6.626070040e-34
const double C = 299792458;         //Speed of light in vacuum (meters per second)
const double K = 1.3806488e-23;     //Boltzmann's constant (Joules per Kelvin) 1.3806488e-23
const double nm_to_m = 1e-9;        //conversion between nm and m
const int interval = 1;             //wavelength interval to calculate at (nm), to change this line 45 also need to be changed

bbresults* blackbody(int min, int max, double temperature) {
    double new_valu, old_valu = 0;      //variables for normalising result
    bbresults *SPD;
    SPD = malloc(sizeof(bbresults));
    //allocate memory based on wavelength bounds
    SPD->wavelength = malloc(sizeof(int) * (max - min));
    SPD->radiance = malloc(sizeof(long double) * (max - min));
    SPD->normalised = malloc(sizeof(long double) * (max - min));

    for (int i = 0; i < (max - min); i++) {
        //Computes radiance for every wavelength of blackbody of given temperature
        SPD->wavelength[i] = min + (interval * i);
        SPD->radiance[i] = ((2 * H * pow(C, 2)) / (pow((SPD->wavelength[i] * nm_to_m), 5))) * (1 / (expm1((H * C) / ((SPD->wavelength[i] * nm_to_m) * K * temperature))));

        //Copy SPD->radiance to SPD->normalised
        SPD->normalised[i] = SPD->radiance[i];

        //Find largest value
        if (i <= 0) {
            old_valu = SPD->normalised[0];
        } else if (i > 0){
            new_valu = SPD->normalised[i];
            if (new_valu > old_valu) {
                old_valu = new_valu;
            }
        }
    }

    for (int i = 0; i < (max - min); i++) {
        //Normalise SPD
        SPD->normalised[i] = SPD->normalised[i] / old_valu;
    }

    return SPD;
}

黑体.h

#ifndef blackbody_h
#define blackbody_h

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

//typedef structure to hold results
typedef struct {
    int *wavelength;
    long double *radiance;
    long double *normalised;
} bbresults;

//function declarations
bbresults* blackbody(int, int, double);

#endif /* blackbody_h */

主程序

#include <stdio.h>
#include "blackbody.h"

int main() {
    bbresults *TEST;
    int min = 100, max = 3000, temp = 5000;

    TEST = blackbody(min, max, temp);

    printf("wavelength | normalised radiance |           radiance               |\n");
    printf("   (nm)    |          -          | (W per meter squr per steradian) |\n");
    for (int i = 0; i < (max - min); i++) {
        printf("%4d %Lf %Le\n", TEST->wavelength[i], TEST->normalised[i], TEST->radiance[i]);
    }

    free(TEST);
    free(TEST->wavelength);
    free(TEST->radiance);
    free(TEST->normalised);
    return 0;
}

输出图:

输出图

于 2017-03-25T10:39:36.687 回答