This is a very simple program, it just calculate the area of a triangle using Heron's formula, but I wanted to over complicate things a bit, so I made some functions that have pointers as parameters, which, in this case, they pretty much work like c references.
It works like that : it takes 3 sides (a,b,c) and calculates the area. The only problem is, whenever I enter a, I receive as an output -nan
, which I did not understand why this happened.
When this sort of thing happens, is easy to blame the pointers, although is more likely the scanf
or printf
functions aren't behaving well.
If anyone could please explain to me why this is happening and how I can fix things up, I would be much thankful.
My code is bellow :
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
void CalculateSemiPerimeter(double a, double b, double c, double *result)
{
*result = ((a b c)/2.0);
}
void heron(double *semiPerimeter, double a, double b, double c, double *result) //note that semiPerimeter and result have the same adresses
{
double sub1 = (*semiPerimeter - a);
double sub2 = (*semiPerimeter - b);
double sub3 = (*semiPerimeter - c);
double mult = (*semiPerimeter*(sub1)*(sub2)*(sub3)); //this part may be wrong
*result = mult;
}
int main()
{
double a, b, c, result;
scanf("%lf, %lf, %lf", &a, &b, &c);
CalculateSemiPerimeter(a,b,c, &result);
heron(&result, a, b, c, &result);
result = sqrt(result);
printf("%.2lf", result);
}
and here's a example of an input :
>>5
which brings as output :
-nan
CodePudding user response:
At least these issues:
Unchecked input
The return value of scanf()
was unchecked. If a,b,c
were successfully written, scanf()
returns 3.
// scanf("%lf, %lf, %lf", &a, &b, &c);
if (scanf("%lf, %lf, %lf", &a, &b, &c) != 3) {
fprintf(stderr, "Input failure.\n");
return EXIT_FAILURE;
}
This would happen if input was "1 2 3"
as the format expects "1,2,3"
- commas between the numbers.
Further, code should sanitize inputs, checking if any are negative.
Advanced: Computational error
With extreme values, say when a
much greater than b
and b
much greater than c
, *semiPerimeter - a
may result in severe loss of precession or even a negative number leading to a later trouble with sqrt(some_negative)
.
Instead code could be more careful and reduce computational errors. Rather than form sub1
with (a b c)/2 - a
, use (-a b c)/2
. This reduces cancellation error.
One level of improvement:
// Return the result and no need to pass in semiPerimeter.
// void heron(double *semiPerimeter, double a, double b, double c, double *result)
double heron(double a, double b, double c) {
double semiPerimeter = (a b c)/2;
double sub1 = (-a b c)/2;
double sub2 = ( a - b c)/2;
double sub3 = ( a b - c)/2;
return semiPerimeter * sub1 * sub2 * sub3;
}
Still with pathological inputs (unreal triangle lengths), the return value of heron()
may be negative.
result = heron(a, b, c);
if (result < 0.0) {
; // TBD code that reports error.
} else {
result = sqrt(result);
...
}
2nd level of improvement:
Sort a, b, c
by magnitude, greatest to least.
Change to below to further reduce cancelation error.
double sub3 = ( a (b - c))/2;