C Code compiles but does not ask for user input - c

I'm a beginner in C doing the harvard cs50x course and this code was written a lot of help but I still can't seem to figure out why it compiles on vscode but does not ask for text from the user.
The prompt is on writing a code that can estimate the difficulty of a text according to grade level based on a given equation that I have included below. Some of the comments include my previous mistakes so they are not descriptions that I purposely added.
#include <cs50.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
// Declare variables to count letters, words, and sentences
// Check if character is an alphabet
// letter++
// If character is not an alphabet or punctuation
// word++
// If character is punctuation
// sentence++
int count_letter = 0;
int count_word = 1;
int count_sentence = 0;
string text = get_string("What is your chosen text?\n");
int text_length = strlen(text);
for(int i = 0; i < text_length; i++) // Run through the length of the text?
{
if(isalpha(text[i])) // check if alphabet
{
count_letter++; // count the number of letters in the text
}
}
for(int i = 0; i < text_length; i++)
{
if(isspace(text[i])) {
count_word++;
}
}
for(int i = 0; i < text_length; i++)
{
// if(text[i]=="." || text[i]=="?" || text[i]=="!")
if(text[i] == '.' || text[i] == '?' || text[i] == '!') {
count_sentence++;
}
}
// L = letters/word*100, S = sentence/word*100
int L = count_letter / count_word * 100;
int S = count_sentence / count_word * 100;
// index = 0.0588 * L - 0.296 * S - 15.8
int index = round(0.0588 * L - 0.296 * S - 15.8);
printf("%d\n", index);
}

In
// L = letters/word*100, S = sentence/word*100
int L = count_letter / count_word * 100;
int S = count_sentence / count_word * 100;
you are doing integer arithmetic, so you are dividing the number of letters by the number of words (the numer of letters is supposed to be larger) but the division is done as integers, so once multiplying it by 100 you will get no decimals (you will get a number ended in two final 00 digits)
you have a chance here, is to multiply first by 100, and then you will get the closest result as an integer and you will get decimals.
Another chance is to convert the numbers to floating point as soon as possible, as in:
// L = letters/word*100, S = sentence/word*100
/* using a floating point literal forces the calculation to be done as real numbers */
int L = 100.0 * count_letter / count_word;
int S = 100.0 * count_sentence / count_word;
or even better (get and use double floating point numbers all the time) after calculations:
// L = letters/word*100, S = sentence/word*100
/* using a floating point number forces the calculation to be done as real numbers */
double L = 100.0 * count_letter / count_word;
double S = 100.0 * count_sentence / count_word;
The data numbers continue to be integers, but as the first operation consists of multiplying a real number (the 100.0 floating point literal) the first product is calculating as floating point (the second argument, the first variable in the expression, is converted to double before doing the calculation, and the third, are both converted to double before doing the calculations) resulting in double precision values.
So finally you can show the result as a floating point number with:
// index = 0.0588 * L - 0.296 * S - 15.8
/* everything is calculated as floating point numbers (L and S have been defined as double above */
double index = round(0.0588 * L - 0.296 * S - 15.8);
/* the formatting specifier must be g below, once index is floating point */
printf("%g\n", index);

Related

Failed to reuse variable in C

I'm trying to code a program that can tell apart real and fake credit card numbers using Luhn's algorithm in C, which is
Multiply every other digit by 2, starting with the number’s
second-to-last digit, and then add those products’ digits together.
Add the sum to the sum of the digits that weren’t multiplied by 2.
If the total’s last digit is 0 (or, put more formally, if the total
modulo 10 is congruent to 0), the number is valid!
Then I coded something like this (I already declared all the functions at the top and included all the necessary libraries)
//Luhn's Algorithm
int luhn(long z)
{
int c;
return c = (sumall(z)-sumodd(z)) * 2 + sumaodd(z);
}
//sum of digits in odd position starting from the end
int sumodd(long x)
{
int a;
while(x)
{
a = a + x % 10;
x /= 100;
}
return a;
}
//sum of all digits
int sumall(long y)
{
int b;
while(y)
{
b = b + y % 10;
y /= 10;
}
return b;
}
But somehow it always gives out the wrong answer even though there's no error or bug detected. I came to notice that it works fine when my variable z stands alone, but when it's used multiple times in the same line of code with different functions, their values get messed up (in function luhn). I'm writing this to ask for any fix I can make to make my code run correctly as I intended.
I'd appreciate any help as I'm very new to this, and I'm not a native English speaker so I may have messed up some technical terms, but I hope you'd be able to understand my concerns.
sumall is wrong.
It should be sumeven from:
Add the sum to the sum of the digits that weren’t multiplied by 2.
Your sumall is summing all digits instead of the non-odd (i.e. even) digits.
You should do the * 2 inside sumodd as it should not be applied to the other [even] sum. And, it should be applied to the individual digits [vs the total sum].
Let's start with a proper definition from https://en.wikipedia.org/wiki/Luhn_algorithm
The check digit is computed as follows:
If the number already contains the check digit, drop that digit to form the "payload." The check digit is most often the last digit.
With the payload, start from the rightmost digit. Moving left, double the value of every second digit (including the rightmost digit).
Sum the digits of the resulting value in each position (using the original value where a digit did not get doubled in the previous step).
The check digit is calculated by 10 − ( s mod ⁡ 10 )
Note that if we have a credit card of 9x where x is the check digit, then the payload is 9.
The correct [odd] sum for that digit is: 9 * 2 --> 18 --> 1 + 8 --> 9
But, sumodd(9x) * 2 --> 9 * 2 --> 18
Here's what I came up with:
// digsum -- calculate sum of digits
static inline int
digsum(int digcur)
{
int sum = 0;
for (; digcur != 0; digcur /= 10)
sum += digcur % 10;
return sum;
}
// luhn -- luhn's algorithm using digits array
int
luhn(long z)
{
char digits[16] = { 0 };
// get check digit and remove from "payload"
int check_expected = z % 10;
z /= 10;
// split into digits (we use little-endian)
int digcnt = 0;
for (digcnt = 0; z != 0; ++digcnt, z /= 10)
digits[digcnt] = z % 10;
int sum = 0;
for (int digidx = 0; digidx < digcnt; ++digidx) {
int digcur = digits[digidx];
if ((digidx & 1) == 0)
sum += digsum(digcur * 2);
else
sum += digcur;
}
int check_actual = 10 - (sum % 10);
return (check_actual == check_expected);
}
// luhn -- luhn's algorithm using long directly
int
luhn2(long z)
{
// get check digit and remove from "payload"
int check_expected = z % 10;
z /= 10;
int sum = 0;
for (int digidx = 0; z != 0; ++digidx, z /= 10) {
int digcur = z % 10;
if ((digidx & 1) == 0)
sum += digsum(digcur * 2);
else
sum += digcur;
}
int check_actual = 10 - (sum % 10);
return (check_actual == check_expected);
}
You've invoked undefined behavior by not initializing a few local variables in your functions, for instance you can remove your undefined behaviour in sumodd() by initializing a to zero like so:
//sum of digits in odd position starting from the end
int sumodd(long x)
{
int a = 0; //Initialize
while(x)
{
a += x % 10; //You can "a += b" instead of "a = a + b"
x /= 100;
}
return a;
}
It's also important to note that long is only required to be a minimum of 4-bytes wide, so it is not guaranteed to be wide enough to represent a decimal-16-digit-integer. Using long long solves this problem.
Alternatively you may find this problem much easier to solve by treating your credit card number as a char[] instead of an integer type altogether, for instance if we assume a 16-digit credit card number:
int luhn(long long z){
char number[16]; //Convert CC number to array of digits and store them here
for(int c = 0; c < 16; ++c){
number[c] = z % 10; //Last digit is at number[0], first digit is at number[15]
z /= 10;
}
int sum = 0;
for(int c = 0; c < 16; c += 2){
sum += number[c] + number[c + 1] * 2; //Sum the even digits and the doubled odd digits
}
return sum;
}
...and you could skip the long long to char[] translation part altogether if you treat the credit card number as an array of digits in the whole program
This expression:
(sumall(z)-sumodd(z)) * 2 + sumall(z);
Should be:
((sumall(z)-sumodd(z)) * 2 + sumodd(z))%10;
Based on your own definition.
But how about:
(sumall(z) * 2 - sumodd(z))%10
If you're trying to be smart and base off sumall(). You don't need to call anything twice.
Also you don't initialise your local variables. You must assign variables values before using them in C.
Also you don't need the local variable c in the luhn() function. It's harmless but unnecessary.
As others mention in a real-world application we can't recommend enough that such 'codes' are held in a character array. The amount of grief caused by people using integer types to represent digit sequence 'codes' and identifiers is vast. Unless a variable represents a numerical quantity of something, don't represent it as an arithmetic type. More issue has been caused in my career by that error than people trying to use double to represent monetary amounts.
#include <stdio.h>
//sum of digits in odd position starting from the end
int sumodd(long x)
{
int a=0;
while(x)
{
a = a + x % 10;
x /= 100;
}
return a;
}
//sum of all digits
int sumall(long y)
{
int b=0;
while(y)
{
b = b + y % 10;
y /= 10;
}
return b;
}
//Luhn's Algorithm
int luhn(long z)
{
return (sumall(z)*2-sumodd(z))%10;
}
int check_luhn(long y,int expect){
int result=luhn(y);
if(result==expect){
return 0;
}
return 1;
}
int check_sumodd(long y,int expect){
int result=sumodd(y);
if(result==expect){
return 0;
}
return 1;
}
int check_sumall(long y,int expect){
int result=sumall(y);
if(result==expect){
return 0;
}
return 1;
}
int main(void) {
int errors=0;
errors+=check_sumall(1,1);
errors+=check_sumall(12,3);
errors+=check_sumall(123456789L,45);
errors+=check_sumall(4273391,4+2+7+3+3+9+1);
errors+=check_sumodd(1,1);
errors+=check_sumodd(91,1);
errors+=check_sumodd(791,8);
errors+=check_sumodd(1213191,1+1+1+1);
errors+=check_sumodd(4273391,15);
errors+=check_luhn(1234567890,((9+7+5+3+1)*2+(0+8+6+4+2))%10);
errors+=check_luhn(9264567897,((9+7+5+6+9)*2+(7+8+6+4+2))%10);
if(errors!=0){
printf("*ERRORS*\n");
}else{
printf("Success\n");
}
return 0;
}

Changing float numbers to int numbers in c

In some part of my program, I have an array which contains float numbers (for example 9.8, 5.0, 4.45). What I have to do is take those numbers to another array and if they are float (I mean if they have digits after the decimal point that are not zero), then multiply them by 10 enough times to make it an int value (so from the first example, after that I should have 98, 5, 445). I know it doesn't sound clear but it's hard for me to describe my problem better.
My attempt was to do this but I have errors.
for(i=0;i<size;i++)
{
if(teb[i]%10!=0)
{
while(teb[i]%1!=0)
{
teb[i]=teb[i]*10;
}
}
}
I have error: invalid operator to binary % (have float and int) and I am not sure if I can use char or similar variables.
In my opinion, you'll not be able to manage this sanely without using string formatting and then converting from string back to floating-point. I use double rather than float. This is the code I came up with, including your three test values as the first three.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static inline int all_same_as(char *str, char c)
{
while (*str != '\0')
{
if (*str++ != c)
return 0;
}
return 1;
}
static inline void zap_trailing(char *str, char c)
{
char *end = str + strlen(str);
while (end > str)
{
if (*(end - 1) != c)
return;
*--end = '\0';
}
}
static inline void zap_leading(char *str, char c)
{
char *end = str;
while (*end == c)
end++;
if (end > str)
memmove(str, end, strlen(end) + 1); /* Not memcpy()! */
}
int main(void)
{
double d[] =
{
9.8,
5.0,
4.45,
1.0,
18.0,
9.25,
8.719,
2.78128,
2721.0/1001.0,
3.14159,
355.0/113.0,
0.001234, /* Smaller */
1.2345E+13, /* Biggest */
1.2345E-13, /* Smallest */
};
enum { NUM_D = sizeof(d) / sizeof(d[0]) };
for (int i = 0; i < NUM_D; i++)
{
char buffer1[32];
snprintf(buffer1, sizeof(buffer1), "%.6f", d[i]);
char buffer2[32];
strcpy(buffer2, buffer1);
zap_leading(buffer2, ' '); /* Not needed with %.6f; was needed with %10.6f */
char *dot = strchr(buffer2, '.');
if (all_same_as(dot + 1, '0'))
*dot = '\0';
else
{
zap_trailing(dot + 1, '0');
size_t len = strlen(dot + 1);
memmove(dot, dot + 1, len + 1); /* Not memcpy()! */
}
double iv = strtod(buffer2, NULL);
printf("%8g = %8.2g = %8.3g = %10s (converted: %10s - [%s] %g)\n",
d[i], d[i], d[i], buffer1, buffer2, buffer2, iv);
}
return 0;
}
Output:
9.8 = 9.8 = 9.8 = 9.800000 (converted: 98 - [98] 98)
5 = 5 = 5 = 5.000000 (converted: 5 - [5] 5)
4.45 = 4.5 = 4.45 = 4.450000 (converted: 445 - [445] 445)
1 = 1 = 1 = 1.000000 (converted: 1 - [1] 1)
18 = 18 = 18 = 18.000000 (converted: 18 - [18] 18)
9.25 = 9.2 = 9.25 = 9.250000 (converted: 925 - [925] 925)
8.719 = 8.7 = 8.72 = 8.719000 (converted: 8719 - [8719] 8719)
2.78128 = 2.8 = 2.78 = 2.781280 (converted: 278128 - [278128] 278128)
2.71828 = 2.7 = 2.72 = 2.718282 (converted: 2718282 - [2718282] 2.71828e+06)
3.14159 = 3.1 = 3.14 = 3.141590 (converted: 314159 - [314159] 314159)
3.14159 = 3.1 = 3.14 = 3.141593 (converted: 3141593 - [3141593] 3.14159e+06)
0.001234 = 0.0012 = 0.00123 = 0.001234 (converted: 0001234 - [0001234] 1234)
1.2345e+13 = 1.2e+13 = 1.23e+13 = 12345000000000.000000 (converted: 12345000000000 - [12345000000000] 1.2345e+13)
1.2345e-13 = 1.2e-13 = 1.23e-13 = 0.000000 (converted: 0 - [0] 0)
You can make choices about how many decimal digits to support, etc. I chose to use up to 6 after the decimal point (format %.6f used with snprintf()). I included very small (1.23E-13) and very large (1.23E+13) values; the behaviour for even bigger or smaller values is similar.
I initially used %10.6f in the snprintf() statement. When I did that, values were printed with leading blanks. The zap_leading() function removes those. The code has since been revised to use %.6f and there are no leading blanks to zap. I left the code to zap leading characters in place; it could be removed too.
Most decimal fractions expressed as some finite number of digits in base 10 cannot be stored exactly in the binary floating point representations typically used in most implementations.
If you know the numbers are only supposed to be accurate to a fixed number of decimal places n, you can first multiply by 10n and round to the nearest integer. Then strip off up to n trailing zero digits.
For example, for up to 3 decimal places:
for(i=0;i<size;i++)
{
long x = round(teb[i] * 1000.0);
for (int j = 0; j < 3; j++)
{
if (x % 10 == 0)
{
x /= 10;
}
else
{
break;
}
}
teb[i] = x;
}
Normally I don't provide answers to homework problems, but since this is such a bad problem, I'm making an exception.
Before I proceed, let me say that the original problem is meaningless and impossible. Since it's impossible, there's no good way to solve it. So the program I'm going to present has a number of problems, some quite serious.
The core of the program does what the problem asks: As long as the fractional part of one of the numbers is not 0, it multiplies the number by 10. Computing "the fractional part of the number" is performed by an auxiliary function I've written, fractionalpart(). First we'll look at the program, then I'll explain its remaining problems.
#include <stdio.h>
float in[] = {9.8, 5.0, 4.45};
int out[3];
float fractionalpart(float f)
{
return f - (int)f;
}
int main()
{
int i;
for(i = 0; i < 3; i++) {
float tmp = in[i];
while(fractionalpart(tmp) > 0.001) {
tmp = tmp * 10;
}
out[i] = tmp;
}
printf("out:\n");
for(i = 0; i < 3; i++) printf("%d\n", out[i]);
}
The problems this has relate to the fact that, on an ordinary computer using ordinary floating-point representations, there is no such number as 9.8. There's no such number as 4.45, either. What you thought was the float number 4.45, for example, is represented internally as something like 4.44999980926513671875. So a "proper" version of this program would multiply it by 10 a total of twenty times, converting it to 444999980926513671875. But that number won't even fit in a 64-bit integer!
So this program cheats: it doesn't loop until the fractional part is exactly 0; it instead loops until the fractional part is less than 0.001. Where did that number come from? The answer is: nowhere, I pulled it out of the air, it seemed like a good guess. You can experiment with bigger or smaller fudge factors if you like.
The bigger problem -- and you've probably thought of this already -- is that the program assumes that the "fractional part" actually does go down to something like 0.001 or 0.0001. But that's not necessarily true! As we just saw, the number 4.45 is "really" 4.44999980926513671875 inside, so after multiplying by 10 twice, it looks like the program is going to have an integer value of 444 (not 445), and the fractional part is going to be 0.999980926513671875, which is not less than 0.001, so the program is going to keep going!
Actually, it doesn't keep going, it does get the "right answer", at least on my machine (and I think I know why), but there are probably plenty of numbers (roughly half of them, actually) where there will be an intermediate result more like 0.999 than 0.001, and this program will do the wrong thing.
I hate posting code with serious problems like this, it's very tempting to try to write an "improved" version, but it would be much messier, and really, the original question is such a horrible one that it just isn't worth it. (It would be instructive to figure out and explain why this program does seems to work, at least most of the time -- why it doesn't get confused by the occasional 0.999 value -- but I don't have time for that just now, either.)

How to convert a float to a string without sprintf in C?

My code is taking a string format and compose it into a buffer (without sprintf, itoa etc).
I'm having trouble converting a float number to string, as I need to have precision of at most 7 digits with no trailing zeros. as well as convert each number in the float variable to char (but in that matter I don't need any help).
I tried several methods, all including math calculations, but nothing has brought me to the desired outcome.
Here's my code so far, but it is messy and also sometimes gives incorrect outcome. I also believe there is a more clean and less-complicated way to do it.
any help will be widely appreciated.
if (*format == 'f') {
float f = *(float*)ptrs[counter];
char str[30];
int b, c, m, k = 7, i = 0, l = 0;
int a = f;
f -= a;
while (k >= 0) {
l = pow(10, k);
m = a / l;
if (m > 0) {
break;
}
k--;
}
printf("%d", k);
for (l = k + 1; l > 0; l--) {
b = pow(10, l - 1);
c = a / b;
str[i++] = c + 48;
a %= b;
}
str[i++] = '.';
for (l = 0; l < 7; l++) {
f *= 10.0;
b = f;
str[i++] = b + 48;
f -= b;
}
for (i = 0; i < 7; i++) {
*buffer = str[i];
buffer++;
}
counter++;
str[i] = '\0';
for example:
input: float v2 =0.9372;
output: .937199
desired output: 0.9372
input: float v2 =0.25000;
output: 1.25000
desired output: 0.25
he's messy and also sometimes gives incorrect outcome
At some point given the base 2 nature of typical floating point, the programmer needs to make choices:
Short code that gets close to the correct answer.
Exact correct conversion with a fair amount of code. e.g. Function to print a double - exactly
Something in between.
With common floating point encoding, there are also issues of
Infinity
Not-a-number.
Oddities like -0.0
And how portable to make the code?
Sources of inexactness
OP's use of int limits float to about [INT_MIN...INT_MAX]. Code fails for float much outside that range. Could use long long to get some more range without a great deal of code change. Better yet research float modff(float value, float *iptr).
float f = ...;
// int a=f;
long long a=f;
Repeated use of f*=10.0 below with a fractional value in f injects a possible rounding (inexactness), at each iteration.
for(l=0;l<7;l++) {
f*=10.0;
Code makes no effort to round given that f may not be 0.0 after the fraction forming for(l=0;l<7;l++) { f*=10.0; loop. I see this as a place to improve precision. This area is also tricky as the round-up may effect many leading digits when they are 9, eventually needing a shift of the string buffer. In the 0.93721, after this loop, f was about 0.74. Since more than 0.5, a rounding would have given the answer of ".9371999" --> ".9372000".
Code aims for 7 decimal digits after the .. Values, as a decimal in code, assigned to a float match to 6 to 9 significant decimal places - which includes digits to the left of .. Expectations should not get too high.
Improvement idea
When the number of fraction digits desired is small, perform 1 scaling/round
// ASSUME f >= 0.0, (sign handling done before here)
long long a=f;
f-=a;
int int_f = round(f*pow(10, 7));
if (int_f < 10000000) {
later_printf_the_7_digits(int_f);
} else {
a++;
later_printf_the_7_digits(0);
}

Floating point exception CS50 readability assignment

I'm facing some problems regarding the Floating point exception error because previously when i printed the variables individually so i wasn't sure what went wrong. Down below is the code i have written so far. I'm not sure what cause the error as i am pretty sure i'm not dividing by zero.
#include <stdio.h>
#include <cs50.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
int main(void)
{
//promt user for text
string n = get_string("Text: ");
int sumletters = 0;
int sumdots = 0;
int sumspaces = 1;
for (int i = 0; i < strlen (n); i++ )
{
if (n[i] >= 'A' && n[i] <= 'z')
{
sumletters = sumletters + 1;
}
if (n[i] == ' ')
{
sumspaces = sumspaces + 1;
}
if (n[i] == '.' || n[i] == '?' || n[i] == '!' )
{
sumdots = sumdots + 1;
}
float L = sumletters / (sumspaces / 100);
float S = sumdots / (sumspaces / 100);
int index = round ((0.0588 * L) - (0.296 * S) - 15.8);
if (index < 1)
{
printf ("Before grade 1\n");
}
{
printf ("Grade 16+\n");
}
else
{
printf ("Grade %i", index);
}
}
printf ("\n");
}
This is the part at fault:
sumletters / (sumspaces / 100)
sumspaces / 100 uses integer division so it will almost always be 0. You can refactor your code to use floating point division by simply distributing the division:
sumletters / sumspaces * 100
The below is integer division and the quotient is often zero as int math discards the fraction.
int sumspaces;
...
sumspaces / 100
The below can result in int division of 0.
int sumletters;
...
// v---------------v might be 0
sumletters / (sumspaces / 100);
Floating point exception error ... i am pretty sure i'm not dividing by zero.
For various reason of a common error handler, int division by zero may be reported as a floating point exception.
As with many operations were the result type is not in the operation, but only the assignment, such problems are common.
Instead, insure the math uses the destination type.
// v---------------v int division
// float L = sumletters / (sumspaces / 100);
float L = sumletters / (sumspaces / 100.0f);
// ^------------------^ At least float division
Aside: No good reason to use float types here, just use double.
Notice that code uses double math, and not float math in:
// v----v v---v v--v double constants
round ((0.0588 * L) - (0.296 * S) - 15.8)
// ^---^ double function

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