EQUATION SOLVER absurd result despite correct algorithm - c

#include<stdio.h>
#include<math.h>
#include<stdlib.h>
void bisect(float *p,int n,int a);
float value(float *p,int n,int a);
int main()
{
int a,i;
float *p;
printf("enter the degree of the polynomial\n");
scanf("%d",&a);
p=(float *) malloc(a*sizeof(float));
for(i=0;i<=a;i++)
{
printf("enter the coefficient of x^%d\n",i);
scanf("%f",p+i);
}
printf("%f\n",value(p,-2,a));
printf("%f\n",value(p,1,a));
printf("%f\n",value(p,0,a));
for(i=-100;i<100;i++)
{
if(value(p,i,a)*value(p,i+1,a)==0.000)
{
printf("%d\n",value(p,i+1,a));
if(value(p,i,a)==0&&value(p,i+1,a)==0.00)
{
printf("the roots are %d,%d\n",i,i+1);
}
if(value(p,i+1,a)==0.0)
{
printf("the real root is %d\n",i+1);
i++;
continue;
}
}
if(value(p,i,a)*value(p,i+1,a)<0)
{
bisect(p,i,a);
}
}
return 0;
}
float value(float *p,int n,int a)
{
float sum=0.0;
int i;
for(i=0;i<=a;i++)
{
sum=sum+*(p+i)*pow(n,i);
}
return sum;
}
void bisect(float *p,int n,int a)
{
float j,k,l;
int i;
j=n;k=n+1;l=(j+k)/2;
for(i=0;i<50;i++)
{
if(value(p,j,a)*value(p,l,a)==0){break;}
if(value(p,j,a)*value(p,l,a)<0)
{
j=j;k=l;l=(j+k)/2;
}
else if(value(p,l,a)*value(p,k,a)<0)
{
l=(l+k)/2;j=l;
}
}
printf("the root of the equation is %f\n",l);
}
I tried inserting print statements in the main function, and found that the value function is giving absurd results for simple polynomials, but the roots are correct for some polynomials but wrong for many. Why would the roots be correct for some if the algorithm was wrong?

There are so many problems in your code:
In main method,
printf("%d\n",value(p,-2,a));
Compiler should give you warning:
warning: format '%d' expects argument of type 'int', but argument 2 has type 'double'
Use %f instead of %d as value() returns float.
You are not allocating enough space and you are casting the return value of malloc. Casting the return value of malloc should be avoided since malloc returns a void * (which means that it needs no cast) and casting the return value can conceal errors. You can read more about this issue here.
p = (float *) malloc(a*sizeof(float));
to
p=malloc((a+1) * sizeof *p);
You are comparing floating ponit number here:
if(value(p,i,a)==0&&value(p,i+1,a)==0.00)
Don't do this, read What Every Computer Scientist Should Know About Floating-Point Arithmetic for the reason. You can use one of this (e.g nearlyEqual()) functions for your purpose.
In method bisect():
j=j;k=l;l=(j+k)/2; // j = j, kidding?

The biggest conceptual or mathematical error (apart from the programming errors explained in the other answer) is that you use integers for the arguments in the value function. It is rarely the case that random polynomials have integer roots. Rename n to x for intuitive readability and set the type of x to float.
Check again your assignment, if the prototype is really value(p,n,a), then maybe the intention is that n is the degree and a the evalution point, thus the signature should be (*float,int,float).
Using this signature, you should really use the Horner scheme to evaluate densely coded polynomials
float value(float *p, int deg, float a) {
int i;
float val = p[deg];
for(i=deg; i-- > 0; ) val = val*a+p[i];
return val;
}
and use descriptive names for variables in the bisection method (instead of j,k,l usually associated to integers) to avoid mixing them up
float left=a, right = a+1, mid =(left+right)/2;
etc.

Related

Wrong Output while finding the root of the equation in C

The following is the C code for calculating the roots of an equation using the Newton-Raphson Algorithm:
#include<stdio.h>
#include<math.h>
double function(double x);
double derivative(double x);
int main(){
double guess=3;
for(int i=0;i<100;i++){
double val=function(guess);
double d=derivative(guess);
guess=guess-val/d;
if(abs(function(guess))<0.00005){
printf("%lf",guess);
break;
}
}
return 0;
}
double function(double x){
return pow(x,5)+2; //f(x)=x^5+2
}
double derivative(double x){
double h=0.00001;
return (function(x+h)-function(x))/h;
}
But the output after compilation is -1.208869, which actually does not satisfy the condition that abs(f(x))<0.00005. What am I doing wrong here?
Your abs(function(guess)) call returns an integer value (cppreference), which will be truncated to zero if the value is less than 1 (as it is when your program stops).
What you need is the fabs() function, to return the absolute value of its given argument as a double.
Changing your if block to use this will work:
if (fabs(function(guess)) < 0.00005) { // Use "fabs" ... not "abs"
printf("%lf", guess);
break;
}

C Power function negative exponent without pow()

I'm trying to make a little power calculator for learning purposes in C without using pow,
but it always returns 0.00 when exponent is negative, please help.
full code:
#include<stdio.h>
//* power caculator function
int power(x,y)
{
float p=1.00;
int i;
if (y<0){
y=-1*y;
x=1/x;
}
for (i=1;i<=y;i++)
{
p=p*x;
}
return p;
}
//* main gets input, calls power caculator and prints result'
int main()
{
int b;
int e;
float p;
printf("enter base");
scanf("%d",&b);
printf("enter exponent");
scanf("%d",&e);
p=power(b,e);
printf("%d to the power of %d is %.2f",b,e,p);
return 0;
}
//* I am NOOB
You are using integers to hold decimal values, in this case with x and with the return type of the power function.
try:
float power(x,y)
{
float p=1.00;
float xx = (float)x;
int i;
if (y<0){
y=-1*y;
xx=1/xx;
}
for (i=1;i<=y;i++)
{
p=p*xx;
}
return p;
}
define data types of x and y explicitly and then adjust the return data type.

Recursion function; "...has stopped working"

I am trying to make a program that gives a specific sum until some point that I define, here it is:
float sum(int n,float m);
main(void) {
float a,m=1.0;
int n;
scanf_s("%ld", &n);
a = sum(n, m);
printf("%f", a);
}
float sum(int n, float m) {
if ((n/m) < 0.0005) {
return 0;
}
else {
return n/m + sum(n, m + 2);
}
}
(Notice that the point I defined is 0.0005) When I give a value bigger or equal to 5, program gives me this error:
...has stopped working
Also, when I increase the defined point to like 0.5, the number of values I can give increases too. Why do you think is this happening and how can I fix it?
The %ld format specifier to scanf_s expects a long int * argument. What you're passing in is a int *. These types are incompatible. Using the wrong format specifier invokes undefined behavior, which in this case manifests as a crash.
The proper format specifier for an int * is %d:
scanf_s("%d", &n);
EDIT:
The crash you're seeing is probably a stack overflow. The sum function will recursively call itself 1000 * n times. I see a similar error under MSVC but at a different limit. You can get around this by going with an iterative solution:
float sum(int n, float m){
float result = 0;
while ((n/m) >= 0.0005){
result += n/m;
m+=2;
}
return result;
}

how to calculate the sum of successive integers starting at 1 and ending at n by using a recursion function

My function gets a number and returns the sum of the numbers before the input including the input; however, i was wondering if it is possible to calculate the sum of successive integers starting at 1 and ending at n(as input)
#include<stdio.h>
int sum(int x){
if(x>0)return x+sum(x-1);
else return 0;
}
main(){
int x;
scanf("%d",&x);;
printf("%d\n\n",sum(x));
}
I found the answer for my question, but the stackoverflow.com don't let me answer it. So, I'll answer it here:
It is really simple, all it needs is another argument for incrementation and the other one to keep track of the value inputed.
#include<stdio.h>
int sum(int x,int t){
if(t<=x) return t+sum(x,t+1);
else return 0;
}
main(){
int x;
printf("enter int: ");
scanf("%d",&x);
printf("%d\n",sum(x,0));
}
The sum of all the intergers smaller than n, and larger than 0 can be found with
int sum = (n*(n+1))/2
which has much less overhead than a recursive function. But if you really want to then you function looks correct, I'd add some curly braces though:
int sum(int x){
if(x>0) {
return x+sum(x-1);
}
else {
return 0;
}
}
The problem with the above function, is that is uses the stack for for memorisation, so you probably won't be able to calculate large ns. You can make your function tail recursive:
int sum(int x, int sum){
if(x>0) {
return sum(x-1, sum + x);
}
else {
return sum;
}
}
This wont use the stack to memorise your intermediate sums. However a simple loop is probably better, and if you want it to look really cryptic and effective, you can do:
int sum = (n*(++n))>>1

-1.#IND00 output for certain input values

I'm trying to write a code that will take x as input and give cos(x) as output, using maclaurin's series.I'm using a while loop until the difference of two consecutive results is less then 0.001. I'm using double type to accomodate larger values.
the code works when x is in range [-2,2], but if x is greater or less than this range the ouput is -1.#IND00. Why is it happening? is the output value out of range ? how can i fix this ??
my code is :
#include <stdio.h>
double abs(double a);
double power(double p, int q);
int fact(int a);
int main()
{
int i=1,j=2*i;
double x,s=1.0,p,l=0.001;
printf("Enter x: ");
scanf("%lf", &x);
p = s+ power(-1,i) * power(x,j) / fact(j);
while (abs(p-s)>l){
i++; j=2*i;
s=p;
p = s+ power(-1,i) * power(x,j) / fact(j);
}
printf("cos(%f) = %f", x,p);
return 0;
}
double abs(double a)
{
if (a>=0) return a;
else return (-a);
}
double power(double p, int q)
{
int i;
double a=1.0;
for (i=0; i<q; i++){
a=a*p;
}
return a;
}
int fact(int a)
{
int i,p=1;
if (a==0 || a==1) return 1;
else
while (a!=1){
p=p*a;
a--;
}
return p;
}
update your scanf function to
scanf("%lf", &x);
Also you need to check pow and fact, these functions could overflow. Especially, fact which only use int.
As a larger |x| is use, more terms are needed and fact() overflows and strange results follow. Use double.
// int fact(int a)
double myfact(double p, int q) {
int i;
double a = 1.0;
for (i=0; i<q; i++){
a=a*p;
}
return a;
}
Eventually with values somewhere larger |x| > 30, other limitations kick in using this method. The limitation is due to precision and not range. For large values a significantly different algorithm should be used.
Potential conflict between int abs(int j) in <stdlib.h>. The prototyped may be found via stdio.h and conflicts with OP double abs(double a). In any case, abs() is a standard library function and OP should avoid that function name. Also recommend renaming power().
// double abs(double a)
double myabs(double a)

Resources