Some sort of issue with simple int/double operations - c

I am new to C and I tried to put together a (only slightly) nontrivial piece of code, in which I use an array to store the values of atan(x) in bins of width dx from 0 to 1, then use the rectangular method to compute the integral of atan(x)dx from 0 to 1. The code should loop while successively making dx smaller to see the integral converge to the analytical result. I have been totally unable to figure out why I get expected outputs for most entries in the loop, but (some number)e+268 for the 7th/8th/15th outputs; I imagine it has something to do with int/double casting but the irregularity of it is very strange to me.
#include <stdio.h>
#include <math.h>
int main() {
int array_len = 20;
int array_len_new;
int num_conv = 18;
double linspace[200];
double conv_array[200];
double PI = 3.142857;
double result;
int i;
int j;
for (i = 0; i < num_conv; i++) {
array_len_new = array_len + 10*i;
double dx = 1./array_len_new;
for (j = 0; j < array_len_new; j++) {
linspace[j] = j* 1.0/array_len_new;
conv_array[i] += atan(linspace[j])*dx;
}
printf("Result for %d bins is: %e\n", array_len_new, conv_array[i]);
}
printf("Converged result: %e\n", (PI-log(4.))/4.0);
return 0;
}
Output:
Result for 20 bins is: 4.190854e-001
Result for 30 bins is: 4.256883e-001
Result for 40 bins is: 4.289811e-001
Result for 50 bins is: 4.309539e-001
Result for 60 bins is: 4.322680e-001
Result for 70 bins is: 4.332061e-001
Result for 80 bins is: 2.308177e+268
Result for 90 bins is: 2.308177e+268
Result for 100 bins is: 4.348934e-001
Result for 110 bins is: 4.352511e-001
Result for 120 bins is: 4.355492e-001
Result for 130 bins is: 4.358013e-001
Result for 140 bins is: 4.360175e-001
Result for 150 bins is: 4.362047e-001
Result for 160 bins is: 2.316093e+268
Result for 170 bins is: 4.365131e-001
Result for 180 bins is: 4.366416e-001
Result for 190 bins is: 4.367566e-001
Converged result: 4.391407e-001
EDIT: I have found that the issue resolves itself if I change the length of conv_array (which only needs 18 values, I made it big because I thought it wouldn't matter) from 200 to 18, or even 100. Why could this possibly be the case?

You do not initialize the elements of conv_array. As a non-static local variable, its initial value is indeterminate. In particular, it is not safe to assume that it will be zero-initialized, but your code relies on exactly that assumption. Declaring it with an initializer should help:
// ...
double linspace[200];
double conv_array[200] = {0};
double PI = 3.14159265358979;
// ...
The = {0} says that the first element is to be initialized to 0, and if any element is initialized then those without explicit initializers are implicitly default-initialized (to 0).
Personally, however, I would not use an array at all where you use conv_array, nor where you use linspace. Try this on for size:
#include <stdio.h>
#include <math.h>
int main() {
int base_num_bins = 20;
int num_cycles = 18;
double PI = 3.14159265358979;
for (int i = 0; i < num_cycles; i++) {
int num_bins = base_num_bins + 10 * i;
double dx = 1. / num_bins;
double integral = 0;
for (int j = 0; j < num_bins; j++) {
integral += atan((double) j / num_bins) * dx;
}
printf("Result for %d bins is: %e\n", num_bins, integral);
}
printf("Converged result: %e\n", (PI - log(4.)) / 4.0);
return 0;
}

conv_array has not been initialised, but you do
conv_array[i] += atan(linspace[j])*dx;
I suggest
double conv_array[200] = { 0 };
Side issue:
double PI = 3.142857;
might be ok with float but it isn't a very accurate value for π when working with double. I suggest using M_PI instead of double PI; and with MSVC you'll also need
#define _USE_MATH_DEFINES

Related

Estimating the value of Pi using the Monte Carlo Method

I am writing a C program that will be able to accept an input value that dictates the number of iterations that will be used to estimate Pi.
For example, the number of points to be created as the number of iterations increases and the value of Pi also.
Here is the code I have so far:
#include <stdio.h>
#include <stdlib.h>
main()
{
const double pp = (double)RAND_MAX * RAND_MAX;
int innerPoint = 0, i, count;
printf("Enter the number of points:");
scanf("%d", &innerPoint);
for (i = 0; i < count; ++i){
float x = rand();
float y = rand();
if (x * x + y * y <= 1){
++innerPoint;
}
int ratio = 4 *(innerPoint/ i);
printf("Pi value is:", ratio);
}
}
Help fix my code as I'm facing program errors.
rand() returns an integer [0...RAND_MAX].
So something like:
float x = rand()*scale; // Scale is about 1.0/RAND_MAX
The quality of the Monte Carlo method is dependent on a good random number generator. rand() may not be that good, but let us assume it is a fair random number generator for this purpose.
The range of [0...RAND_MAX] is RAND_MAX+1 different values that should be distributed evenly from [0.0...1.0].
((float) rand())/RAND_MAX biases the end points 0.0 and 1.0 giving them twice the weight of others.
Consider instead [0.5, 1.5, 2.5, ... RAND_MAX + 0.5]/(RAND_MAX + 1).
RAND_MAX may exceed the precision of float so converting rand() or RAND_MAX, both int, to float can incurring rounding and further disturb the Monte Carlo method. Consider double.
#define RAND_MAX_P1 ((double)RAND_MAX + 1.0)
// float x = rand();
double x = ((double) rand() + 0.5)/RAND_MAX_P1;
x * x + y * y can also incur excessive rounding. C has hypot(x,y) for a better precision sqrt(x*x + y*y). Yet here, with small count, it likely makes no observable difference.
// if (x * x + y * y <= 1)
if (hypot(x, y <= 1.0))
I am sure it is not the best solution, but it should do the job and is similar to your code. Use a sample size of at least 10000 to get a value near PI.
As mentioned in the commenter: You should look at the data types of the return values functions give you.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
// Initialize random number generation
srand(time(NULL));
int samples = 10000;
int points_inside =0;
// Read integer - sample size (use at least 10000 to get near PI)
printf("Enter the number of points:");
scanf("%d", &samples);
for (int i = 0; i < samples; ++i){
// Get two numbers between 0 and 1
float x = (float) rand() / (float)RAND_MAX;
float y = (float) rand() / (float)RAND_MAX;
// Check if point is inside
if (x * x + y * y <= 1){
points_inside++;
}
// Calculate current ratio
float current_ratio = 4 * ((float) points_inside / (float) i);
printf("Current value of pi value is: %f \n", current_ratio);
}
}

Error outputting quotient of array and integer

Code simulates the rolling of two die 36000 times and outputs "Sum = _; Frequency = _; Percentage = _". Compiled code outputs everything correctly except percentage. "Percentage = 0" when it should output the quotient of "(frequency[calcCount] /36000) * 100" Is this a conflict in data type? How can I properly output the quotient?
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SUM_SIZE 36000
#define FREQUENCY_SIZE 13
int main(void){
int rollCount; // counter to loop through 36000 rolls of dice & sums
int calcCount; //counter to loop through frequencies 1-12 of sum calculation
//initialize frequency counters to 0
int frequency[FREQUENCY_SIZE] = {0};
//calculation array
int calculation[SUM_SIZE];
//seed
srand((unsigned)(time(NULL)));
for (rollCount = 1; rollCount <= SUM_SIZE; rollCount++){
//rolling first die
int face1 = (1 + ( rand() % 6));
//rolling second die
int face2 = (1 + ( rand() % 6));
int sum = face1 + face2;
//initializing array elements
calculation[rollCount] = sum;
//for each roll, select value of an element of array calculation
//and use that value as subscript in array frequency to determine
//element to increment (which in this case is the sum frequency)
++frequency[calculation[rollCount]];
}
//displaying results
for (calcCount = 2; calcCount < FREQUENCY_SIZE; calcCount++){
//calculating percentage
int percentage = (frequency[calcCount] /36000) * 100;
printf("Sum = %d; Frequency = %d; Percentage = %d \n", calcCount, frequency[calcCount], percentage);
}
}
When you do a division between two integers, the result will also be an integer and the "exact" result is truncated to fit an integer. Examples:
3/2 -> 1
10/3 -> 3
5/10 -> 0
and when you do
int percentage = (frequency[calcCount] /36000) * 100;
the part frequency[calcCount] /36000 is calculated first. It is a division between two int and it will give the result zero because frequency[calcCount] is less than 36000. Consequently multiplying with 100 still gives zero.
Instead do the multiplication first - like:
int percentage = (100 * frequency[calcCount]) /36000;
Another alternative is to use floating point like:
double percentage = (frequency[calcCount] /36000.0) * 100;
^^^
Notice the .0 to make 36000 a double
but then you need to change the print to use %f
double percentage = (frequency[calcCount] / 36000.0) * 100;
printf("Sum = %d; Frequency = %d; Percentage = %.2f \n", calcCount, frequency[calcCount], percentage);
^^^
Notice this to print the double

What should I change so that my arctan(x) approximation can display x=1 and x=-1 properly?

One of my C assignments was it to write an approximation of arctan(x) in the language C. The equation which I should base it on is
arctan(x)=\sum {k=0}^{\infty }(-1)^{k} \tfrac{x^{2k+1}}{2k+1}
In addition x is only defined as -1<=x<=1.
Here is my code.
#include <stdio.h>
#include <math.h>
double main(void) {
double x=1;
double k;
double sum;
double sum_old;
int count;
double pw(double y, double n) {
double i;
double number = 1;
for (i = 0; i < n; i++) {
number *= y;
}
return(number);
}
double fc (double y) {
double i;
double number = 1;
for (i = 1; i <= y; i++){
number *= i;
}
return(number);
}
if(x >= (-1) && x <= 1) {
for(k=0; sum!=sum_old; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
count++;
printf("%d || %.17lf\n", count, sum);
}
printf("My result is: %.17lf\n",sum);
printf("atan(%f) is: %.17f\n", x, atan(x));
printf("My result minus atan(x) = %.17lf\n", sum - atan(x));
} else {
printf("x is not defined. Please choose an x in the intervall [-1, 1]\n");
}
return 0;
}
It seemingly works fine with every value, except value 1 and -1. If x=1, then the output ends with:
...
7207 || 0.78543285189457468
7208 || 0.78536
Whereas the output should look more like this. In this case x=0.5.
25 || 0.46364760900080587
26 || 0.46364760900080587
My result is: 0.46364760900080587
atan(0.500000) is: 0.46364760900080609
My result minus atan(x) atan(x) = -0.00000000000000022
How can I improve my code so that it can run with x=1 and x=-1.
Thanks in advance.
PS: I use my own created pw() function instead of pow(), because I wanted to bybass the restriction of not using pow() as we didn't had that in our lectures yet.
PPS: I'd appreciate any advice as to how to improve my code.
In each iteration, you add (-1)k • x2k+1 / (2k+1), and you stop when there is no change to the sum.
If this were calculated with ideal arithmetic (exact, infinitely precise arithmetic), it would never stop for non-zero x, since you are always changing the sum. When calculating with fixed-precision arithmetic, it stops when the term is so small it does not change the sum because of the limited precision.
When |x| is less than one by any significant amount, this comes quickly because x2k+1 gets smaller. When |x| is one, the term becomes just 1 / (2k+1), which gets smaller very slowly. Not until k is around 253 would the sum stop changing.
You might consider changing your stopping condition to be when sum has not changed from sum_old very much rather than when it has not changed at all.
if(x >= (-1) && x <= 1) {
for(k=0; sum!=sum_old; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
count++;
printf("%d || %.17lf\n", count, sum);
}
Comparing doubles can be tricky. The conventional way to compare doubles is to test within epsilon. There should be an epsilon value defined somewhere, but for your purposes how many digits are enough to approximate? If you only need like 3 or 4 digits you can instead have
#define EPSILON 0.0001 //make this however precise you need to approximate.
if(x >= (-1) && x <= 1) {
for(k=0; fabs(sum - sum_old) > EPSILON; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
count++;
printf("%d || %.17lf\n", count, sum);
}
If the issue is that -1,1 iterate too many times either reduce the precision or increase the step per iteration. I am not sure that is what you're asking though, please clarify.
I think the cause of this is for a mathematical reason rather than a programming one.
Away from the little mistakes and adjustments that you should do to your code, putting x = 1 in the infinite series of arctan, is a boundary condition:
In this series, we add a negative value to a positive value then a negative value. This means the sum will be increasing, decreasing, increasing, ... and this will make some difference each iteration. This difference will be smaller until the preciseness of double won't catch it, so the program will stop and give us the value.
But in the sum equation. When we set z = 1 and n goes from 0 to ∞, this will make this term (-1^n) equal to 1 in one time and -1 in the next iteration. Also,
the value of the z-term will be one and the denominator value when n approaches infinity will = ∞ .
So the sum several iterations will be like +1/∞ -1/∞ +1/∞ -1/∞ ... (where ∞ here represents a big number). That way the series will not reach a specific number. This is because z = 1 is a boundary in this equation. And that is causing infinite iterations in your solution without reaching a number.
If you need to calculate arctan(1) I think you should use this formula:
All formulas are from this Wikipedia article.
Here is some modifications that make your code more compact and has less errors:
#include <stdio.h>
#include <math.h>
#define x 0.5 //here x is much easier to change
double pw(double, double); //declaration of the function should be done
int main() { //the default return type of main is int.
double k;
double sum = 0 ; //you should initiate your variables.
double sum_old = 1 ; //=1 only to pass the for condition first time.
//you don't need to define counter here
if(x < -1 || x > 1){
printf("x is not defined. Please choose an x in the interval [-1, 1]\n");
return 0;
}
for(k=0; sum!=sum_old; k++) {
sum_old = sum;
sum += pw((-1), k) * pw(x, (2*k) + 1)/((2*k) + 1);
printf("%.0f || %.17lf\n", k, sum);
}
printf("My result is: %.17lf\n",sum);
printf("atan(%f) is: %.17f\n", x, atan(x));
printf("My result minus atan(x) = %.17lf\n", sum - atan(x));
return 0;
}
double pw(double y, double n) { //functions should be declared out of the main function
double i;
double number = 1;
for (i = 0; i < n; i++) {
number *= y;
}
return(number);
}
double fc (double y) {
double i;
double number = 1;
for (i = 1; i <= y; i++){
number *= i;
}
return(number);
}

-nan return value / e (euler) raised to a power calculation loop

I'm learning C programming and made the algorithm below to solve this problem:
The code actually works, but initially the loop was with only 10 repetitions (rep <= 10), and the anwer for p = 3 was almost correct, so I changed rep <= 20. And It gave me just the exact answer from my calculator. And then I tried with a higher number, 12, and the output again was inaccurate. So I ended raising rep <= 35. If I get the loop for higher repetitions I get "-nan", and if the input for p is too high it will be the same. So just have to see the pattern to know that the problem of inaccuracy will get back as I input higher numbers which is not the case because the output will be NaN if I input a high value.
Is it possible to solve it without higher level functions? just want to know if my program is ok for the level in which I am now...
#include <stdio.h>
int main()
{
float p; //the power for e
float power; //the copy of p for the loop
float e = 1; //the e number I wanna raise to the power of p
int x = 1; //the starting number for each factorial generation
float factorial = 1;
int rep = 1; //the repeater for the loop
printf( "Enter the power you want to raise: " );
scanf( "%f", &p );
power = p;
while ( rep <= 35) {
while ( x > 1) {
factorial *= x;
x--;
}
e += p / factorial;
//printf("\nthe value of p: %f", p); (TESTER)
//printf("\nthe value of factorial: %f", factorial); (TESTER)
p *= power; //the new value for p
rep++;
factorial = 1;
x = rep; //the new value for the next factorial to be generated
//printf("\n%f", e); (TESTER)
}
printf("%.3f", e);
return 0;
}
Sorry if I had syntax/orthography errors, I'm still learning the language.
Before we begin, let's write your original code as a function, with some clean-ups:
float exp_original(float x, int rep = 35)
{
float sum = 1.0f;
float power = 1.0f;
for (int i = 1; i <= rep; i++)
{
float factorial = 1.0f;
for (int j = 2; j <= i; j++)
factorial *= j;
power *= x;
sum += power / factorial;
}
return sum;
}
There were some unnecessary variables you used which were removed, but otherwise the procedure is the same: compute the factorial from scratch.
Let's look at the ratio between successive terms in the series:
We can thus simply multiply the current term by this expression to get the next term:
float exp_iterative(float x, int rep = 35)
{
float sum = 1.0f;
float term = 1.0f;
for (int i = 1; i <= rep; i++)
{
term *= x / i;
sum += term;
}
return sum;
}
Seems much simpler, but is it better? Comparison against the C-library exp function (which we assume to be maximally precise):
x exp (C) exp_orig exp_iter
-------------------------------------------
1 2.7182817 2.718282 2.718282
2 7.3890562 7.3890567 7.3890567
3 20.085537 20.085539 20.085539
4 54.598148 54.598152 54.598152
5 148.41316 148.41318 148.41316
6 403.4288 403.42871 403.42877
7 1096.6332 1096.6334 1096.6334
8 2980.958 2980.9583 2980.9587
9 8103.084 8103.083 8103.083
10 22026.465 22026.467 22026.465
11 59874.141 59874.148 59874.152
12 162754.8 162754.77 162754.78
13 442413.41 -nan(ind) 442413.38
14 1202604.3 -nan(ind) 1202603.5
15 3269017.3 -nan(ind) 3269007.3
16 8886111 -nan(ind) 8886009
17 24154952 -nan(ind) 24153986
18 65659968 -nan(ind) 65652048
19 1.784823e+08 -nan(ind) 1.7842389e+08
20 4.8516518e+08 -nan(ind) 4.8477536e+08
The two custom implementations are neck-and-neck in-terms of precision, until x = 13 where the original gives NaN. This is because the highest power term 13^35 = 9.7278604e+38 exceeds the maximum value FLT_MAX = 3.40282e+38. The accumulated term in the iterative version never reaches anywhere near the limit.

Implementing equations with very small numbers in C - Plank's Law generating blackbody

I have a problem that, after much head scratching, I think is to do with very small numbers in a long-double.
I am trying to implement Planck's law equation to generate a normalised blackbody curve at 1nm intervals between a given wavelength range and for a given temperature. Ultimately this will be a function accepting inputs, for now it is main() with the variables fixed and outputting by printf().
I see examples in matlab and python, and they are implementing the same equation as me in a similar loop with no trouble at all.
This is the equation:
My code generates an incorrect blackbody curve:
I have tested key parts of the code independently. After trying to test the equation by breaking it into blocks in excel I noticed that it does result in very small numbers and I wonder if my implementation of large numbers could be causing the issue? Does anyone have any insight into using C to implement equations? This a new area to me and I have found the maths much harder to implement and debug than normal code.
#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';
}
/*********************UPDATE Friday 24th Mar 2017 23:42*************************/
Thank you for the suggestions so far, lots of useful pointers especially understanding the way numbers are stored in C (IEEE 754) but I don't think that is the issue here as it only applies to significant digits. I implemented most of the suggestions but still no progress on the problem. I suspect Alexander in the comments is probably right, changing the units and order of operations is likely what I need to do to make the equation work like the matlab or python examples, but my knowledge of maths is not good enough to do this. I broke the equation down into chunks to take a closer look at what it was doing.
//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;
}
Equation variable values during runtime in xcode:
I notice a couple of things that are wrong and/or suspicious about the current state of your program:
You have defined nm_to_m as 10-9,, yet you divide by it. If your wavelength is measured in nanometers, you should multiply it by 10-9 to get it in meters. To wit, if hh is supposed to be your wavelength in meters, it is on the order of several light-hours.
The same is obviously true for dd as well.
mm, being the exponential expression minus 1, is zero, which gives you infinity in the results deriving from it. This is apparently because you don't have enough digits in a double to represent the significant part of the exponential. Instead of using exp(...) - 1 here, try using the expm1() function instead, which implements a well-defined algorithm for calculating exponentials minus 1 without cancellation errors.
Since interval is 1, it doesn't currently matter, but you can probably see that your results wouldn't match the meaning of the code if you set interval to something else.
Unless you plan to change something about this in the future, there shouldn't be a need for this program to "save" the values of all calculations. You could just print them out as you run them.
On the other hand, you don't seem to be in any danger of underflow or overflow. The largest and smallest numbers you use don't seem to be a far way from 10±60, which is well within what ordinary doubles can deal with, let alone long doubles. The being said, it might not hurt to use more normalized units, but at the magnitudes you currently display, I wouldn't worry about it.
Thanks for all the pointers in the comments. For anyone else running into a similar problem with implementing equations in C, I had a few silly errors in the code:
writing a 6 not a 9
dividing when I should be multiplying
an off by one error with the size of my array vs the iterations of for() loop
200 when I meant 2000 in the temperature variable
As a result of the last one particularly I was not getting the results I expected (my wavelength range was not right for plotting the temperature I was calculating) and this was leading me to the assumption that something was wrong in the implementation of the equation, specifically I was thinking about big/small numbers in C because I did not understand them. This was not the case.
In summary, I should have made sure I knew exactly what my equation should be outputting for given test conditions before implementing it in code. I will work on getting more comfortable with maths, particularly algebra and dimensional analysis.
Below is the working code, implemented as a function, feel free to use it for anything but obviously no warranty of any kind etc.
blackbody.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;
}
blackbody.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 */
main.c
#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;
}
Plot of output:

Resources