C program to find roots error - c

I am writing a function in C with the below specifications:
float find_root(float a, float b, float c, float p, float q);
find_root takes the coefficients a,b,c of a quadratic equation and an interval (p, q). It will return the root of this equation in the given interval.
For example: find_root(1, -8, 15, 2, 4) should produce a root "close to" 3.0
I have written the below code, and I don't understand why it doesn't work:
#include<stdio.h>
#include<math.h>
main()
{
printf("Hello World");
}
float find_root(float a, float b, float c, float p, float q) {
float d,root1,root2;
d = b * b - 4 * a * c;
root1 = ( -b + sqrt(d)) / (2* a);
root2 = ( -b - sqrt(d)) / (2* a);
if (root1<=q || root1>=p)
{
return root1;
}
return root2;
}
Please let me know what the error is.

Your program doesn't work, because, you never called find_root() from your main().
find_root() is not suppossed to run all-by-itself. Your program statrs execution from main(). You need to call your sub-function from main() in order to make them execute.
Change your main to have a call to find_root(), something like below.
int main() //put proper signature
{
float anser = 0;
answer = find_root(1, -8, 15, 2, 4); //taken from the question
printf("The anser is %f\n", answer); //end with a \n, stdout is line buffered
return 0; //return some value, good practice
}
Then, compile the program like
gcc -o output yourfilename.c -lm
Apart from this, for the logical issue(s) in find_root() function, please follow the way suggested by Mr. #paxdiablo.

For that data, your two roots are 5 and 3. With p == 2 and q == 4:
if (root1<=q || root1>=p)
becomes:
if (5<=4 || 5>=2)
which is true, so you'll get 5.
The if condition you want is:
if ((p <= root1) && (root1 <= q))
as shown in the following program, that produces the correct 3:
#include<stdio.h>
#include<math.h>
float find_root (float a, float b, float c, float p, float q) {
float d,root1,root2;
d = b * b - 4 * a * c;
root1 = ( -b + sqrt(d)) / (2* a);
root2 = ( -b - sqrt(d)) / (2* a);
if ((p <= root1) && (root1 <= q))
return root1;
return root2;
}
int main (void) {
printf ("%f\n", find_root(1, -8, 15, 2, 4));
return 0;
}
That's the logic errors with your calculations of the roots.
Just keep in mind there are other issues with your code.
You need to ensure you actually call the function itself, your main as it stands does not.
It also wont produce a value within the p/q bounds, instead it will give you the first root if it's within those bounds, otherwise it'll give you the second root regardless of its value.
You may want to catch the situation where d is negative, since you don't want to take the square root of it:
a = 1000, b = 0, c = 1000: d <- -4,000,000
And, lastly, if your compiler is complaining about not being able to link sqrt (as per one of your comments), you'll probably find you can fix that by specifying the math library, something like:
gcc -o myprog myprog.c -lm

Your program starts at main by definition.
Your main function is not calling find_root but it should.
You need to compile with all warnings & debug info (gcc -Wall -Wextra -g) then use a debugger (gdb) to run your code step by step to understand the behavior of your program, so compile with
gcc -Wall -Wextra -g yoursource.c -lm -o yourbinary
or with
clang -Wall -Wextra -g yoursource.c -lm -o yourbinary
then learn how to use gdb (e.g. run gdb ./yourbinary ... and later ./yourbinary without a debugger)
Then you'll think and improve the source code and recompile it, and debug it again. And repeat that process till you are happy with your program.
BTW, you'll better end your printf format strings with \n or learn about fflush(3)
Don't forget to read the documentation of every function (like printf(3) ...) that you are calling.
You might want to give some arguments (thru your main(int argc, char**argv) ...) to your program. You could use atof(3) to convert them to a double
Read also about undefined behavior, which you should always avoid.
BTW, you can use any standard C compiler (and editor like emacs or gedit) for your homework, e.g. use gcc or clang on your Linux laptop (then use gdb ...). You don't need a specific seashell

Change this condition
if (root1<=q || root1>=p)
to
if (root1<=q && root1>=p)
otherwise if anyone of the conditions is satisfied, root1 will be returned and root2 will almost never be returned. Hope this fixes your problem.

Related

scanf produces segfault when the program is run with a custom entry point (using gcc 7.4.0)

Consider the following code:
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("main\n");
int a;
scanf("%d", &a);
printf("a = %d\n", a);
return 0;
}
int main1() {
printf("main1\n");
int a;
scanf("%d", &a);
printf("a = %d\n", a);
exit(0);
return 0;
}
int main2() {
printf("main2\n");
int a = getchar() - '0';
int b = getchar() - '0';
int c = getchar() - '0';
printf("a = %d\n", 100 * a + 10 * b + c);
exit(0);
return 0;
}
Assuming that the code resides in a file called test.c, the following works fine (it prints "a = 123"):
gcc -o test test.c
echo 123 | ./test
If, however, I run the program with a custom entry point, I get the dreaded Segmentation fault:
gcc -o test test.c -e"main1"
echo 123 | ./test
But if I replace the scanf with three getchars, the program runs fine again despite being run with a custom entry point:
gcc -o test test.c -e"main2"
echo 123 | ./test
To make things even more interesting, these problems occur with gcc 7.4.0 but not with gcc 4.8.4.
Any ideas?
The -e command line flag redefines the actual entry point of your program, not the “user” entry point. By default, using GCC with the GNU C standard library (glibc) this entry point is called _start, and it performs further setup before invoking the user-provided main function.
If you want to replace this entry point and continue using glibc you’ll need to perform further setup yourself. But alternatively you can use the following method to replace the main entry point, which is much simpler:
gcc -c test.c
objcopy --redefine-sym main1=main test.o
gcc -o test test.o
Note, this will only work if you don’t define main in your code, otherwise you’ll get a “multiple definition of `main'” error from the linker.

minimum modification to make a visual studio FORTRAN-C mixed code gfortran-gcc compatible

I'm trying to modify this code (gist as back up) to become gfortran-gcc compatible.
I removed the [VALUE] tags
used POINTER with -fcray-pointer flag for gfortran, instead of the [REFERENCE] tag
removed the __stdcall , because tried the #define __stdcall __attribute__((stdcall)) which caused warning: ‘stdcall’ attribute ignored [-Wattributes]
now this is what I have:
C code CMAIN.C:
#include <stdio.h>
extern int FACT_(int n);
extern void PYTHAGORAS_(float a, float b, float *c);
main()
{
float c;
printf("Factorial of 7 is: %d\n", FACT_(7));
PYTHAGORAS_(30, 40, &c);
printf("Hypotenuse if sides 30, 40 is: %f\n", c);
}
the FORTRAN code FORSUBS.FOR:
INTEGER*4 FUNCTION Fact (n)
INTEGER*4 n
INTEGER*4 i, amt
amt = 1
DO i = 1, n
amt = amt * i
END DO
Fact = amt
END
SUBROUTINE Pythagoras (a, b, cp)
REAL*4 a
REAL*4 b
POINTER (cp, c)
REAL*4 c
c = SQRT (a * a + b * b)
END
the Makefile:
all:
gfortran -c FORSUBS.FOR -fcray-pointer
gcc -c CMAIN.C
gfortran -o result.out FORSUBS.o CMAIN.o
rm -rf *.o
clean :
rm -rf *.out *~ *.bak *.o
However I still get the error:
CMAIN.o: In function `main':
CMAIN.C:(.text+0x1d): undefined reference to `FACT_(int)'
CMAIN.C:(.text+0x4c): undefined reference to `PYTHAGORAS_(float, float, float*)'
I would appreciate if you could help me know:
What is the issue and how can I resolve it?
what is the best method to modify the original code to become gcc-gfortran compatible with minimum change.
P.S.1. also shared on Reddit.
P.S.2. operating system and compiler specifications are same as this question.
Fortran passes variables by reference, as mentioned in the first sentences of any Fortran to C tutorial. So you:
Cannot just remove the [VALUE], you should add the modern VALUE attribute or change the C code to pass pointers.
Should not use pointers on the Fortran side of [REFERENCE], but just remove it.
Use the actual name mangling for your compiler, these days normally subroutine_name_ (appended underscore, name is lowercase) or use the modern bind(C, name="binding_name") attribute.
Without modern Fortran, but with VALUE (it is just a PITA without):
INTEGER*4 FUNCTION Fact (n)
INTEGER*4, VALUE :: n
INTEGER*4 i, amt
amt = 1
DO i = 1, n
amt = amt * i
END DO
Fact = amt
END
SUBROUTINE Pythagoras (a, b, c) bind(C)
REAL*4, VALUE :: a
REAL*4, VALUE :: b
REAL*4 c
c = SQRT (a * a + b * b)
END
and than just change your C names to lowercase (pythagoras_, fact_)... With the VALUE attribute you do not need to introduce all those C temporaries you see in the other answer. The bind(C) is required for VALUE to work properly. It will save you from rewriting the code that calls the Fortran procedures.
For best modern experience use bind(C,name="any_name_you_want") to set the exact linkage symbol name.
In addition to my top comments, Fortran passes by reference, so you have to modify the .c and the .for files.
The code below works. There may be a simpler way to declare things, but this should [at least] get you further along. Caveat: I haven't done much fortran since Fortran IV days, so I'm a bit rusty. I'd defer to Vladimir's VALUE solution as a better way to go.
#include <stdio.h>
#if 0
extern int fact_(int n);
extern void pythagoras_(float a, float b, float *c);
#else
extern int fact_(int *n);
extern void pythagoras_(float *a, float *b, float *c);
#endif
int
main(void)
{
float c;
#if 0
printf("Factorial of 7 is: %d\n", fact_(7));
#else
int n = 7;
printf("Factorial of 7 is: %d\n", fact_(&n));
#endif
#if 0
pythagoras_(30, 40, &c);
#else
float a = 30;
float b = 40;
pythagoras_(&a, &b, &c);
#endif
printf("Hypotenuse if sides 30, 40 is: %f\n", c);
return 0;
}
INTEGER*4 FUNCTION Fact (n)
INTEGER*4 n
INTEGER*4 i, amt
amt = 1
DO i = 1, n
amt = amt * i
END DO
Fact = amt
END
SUBROUTINE Pythagoras (a, b, c)
REAL*4 a
REAL*4 b
REAL*4 c
c = SQRT (a * a + b * b)
END
Here is the program output:
Factorial of 7 is: 5040
Hypotenuse if sides 30, 40 is: 50.000000
UPDATE:
I get the same undefined reference error from your code!
Aha!
One thing I didn't mention [because I didn't think it would make a difference] is that I changed the source file names to use all lowercase letters (e.g. CMAIN.C --> cmain.c and FORSUBS.FOR --> forsubs.for)
With that, the output of nm *.o produces:
cmain.o:
U fact_
0000000000000000 T main
U printf
U pythagoras_
forsubs.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_
The change in the fortran source filename doesn't matter too much. But, the C source filename does!
More to the point it's the filename suffix (i.e. .C changed to .c).
This is because gcc will [try to be smart and] look at the suffix to determine which language the file is written in and compile accordingly. For example, gcc -c foo.cpp will compile the file as if it is written in c++ and not c, just as if the command were: g++ -c foo.cpp
Although .cpp is the [more] usual suffix for a c++ filename, an alternate suffix for c++ files is: .C
That is, most projects use the .cpp convention, but some use the .C convention. One of the reasons for preferring .cpp over .C is that Windows filesystems are case insensitive. So, .C and .c would appear the same. However, POSIX systems (e.g. linux, macOSX, iOS, android, etc.) have case sensitive filenames so either convention can be used.
So, gcc -c CMAIN.C will compile as c++. This does c++ style "name mangling" of symbols--not what we want. In c++, the mangling is done to allow "overloading" of function names. That is, two [or more] different functions can have the same name, as long as they use different arguments. For example:
void calc(int val);
void calc(int val1,int val2);
void calc(double fval);
Here is the output of nm *.o if we use CMAIN.C:
CMAIN.o:
0000000000000000 T main
U printf
U _Z11pythagoras_PfS_S_
U _Z5fact_Pi
FORSUBS.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_
Running that file through c++filt to "demangle" the c++ names we get:
CMAIN.o:
0000000000000000 T main
U printf
U pythagoras_(float*, float*, float*)
U fact_(int*)
FORSUBS.o:
0000000000000000 T fact_
0000000000000045 T pythagoras_
So, try to use lowercase filenames if possible [that is recommended best practice]. But, at a minimum, don't use .C

giving parameters into math function

If I call in C, math function `"trunc" in math library is define as:
extern double trunc _PARAMS((double));
and in my main file call it:
int i = (int)trunc(2.5);
It works fine, no problems. But if I try to pass double passively, like:
double d = 2.5;
int i = (int)trunc(d);
It won't work?!? In my microprocessor STM32F4 IDE it goes in debugger mode into:
Infinite_Loop:
b Infinite_Loop
and it stuck there. I also change double and try float, int, unit8_t,... no one isn't working.
Also other math functions will work fine as I call them like this:
i = sin(1);
i = cos(1);
But, it will crashed the same, if called like this:
int a = 1;
i = sin(a);
i = cos(a);
I am running this code on microprocessor STM32F4 Discovery,IDE is Eclipse Ac6.
If you refer the man page for trunc() you should note that, it says
Link with -lm
So, make sure that you are linking with -lm.
Secondly, when synopsis say double trunc(double x);, there is no point in trying it out with other data types.
If you want to try with other data types then you should look for these variants:
float truncf(float x);
long double truncl(long double x);
Test code:
#include<stdio.h>
#include<math.h>
int main(void)
{
// using trunc function which return
// Truncated value of the input
double d = 2.7;
printf(" Truncated value is %d \n", (int)trunc(d));
return 0;
}
This generated an output:
Truncated value is 2
And it was compiled with gcc version 5.3.0
When I compile the same code gcc version 7.2.0 without -lm option,
I get following warning:
undefined reference to `trunc'
error: ld returned 1 exit status

Codeblocks compile different my program than gcc in comand

I was trying today to check an Answer and I realized that if i use codeblocks (with gcc) i have to treat the error different from the command line (Ubuntu Linux) using gcc.
The program is like this:
#include<stdio.h>
#include<math.h>
int main(void){
double len,x,y =0;
int n=123456;
len=floor(log10(abs(n))) + 1;
x = n / pow(10, len / 2);
y = n - x * pow(10, len / 2);
printf("First Half = %f",x);
printf("\nSecond Half = %f",y);
return 0;
}
And if i try to compile it i get:
error: implicit declaration of function ‘abs’ [-Werror=implicit-function-declaration]|
So here is the funny thing. I added -lm to the Compiler => global compiler => settings => Other settings, but the result is the same.
It is working only if i include stdlib.h.
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
int main(void){
double len,x,y =0;
int n=123456;
len=floor(log10(abs(n))) + 1;
x = n / pow(10, len / 2);
y = n - x * pow(10, len / 2);
printf("First Half = %f",x);
printf("\nSecond Half = %f",y);
return 0;
}
But if I use command line (in terminal) using the comand:
gcc program.c -o program -lm
The program compiled successfully.
My question: Why happens this ?
I did a research on interent and found that some people says the abs function is declared in stdlib.h, not math.h. but if i compile in command line (without including stdlib.h) with -lm works. I'm confused.
Short answer: Try
gcc -Wall -Wextra -pedantic -o program -lm
or
gcc -Wall -Wextra -Werror -pedantic -o program -lm
to make it fail on warnings as Codeblocks seems to do.
Long answer: Linking to a library is a completely different matter than including a header file. In C, for historic reasons, it is "allowed" to use a function that is not declared. The compiler in this case assumes a function returning int and taking whatever arguments you give it. For abs(), these assumptions hold. So later, the linker finds the function when linking with libm and everything is fine.
But there are quite some catches: First you will miss simple typos if you don't enable warnings. Second, the compiler is unable to check the arguments you give -> crashing program ahead. And even more problems are to expect if the function does return something other than int.
abs() is declared in stdlib.h. To use it, include this header. And always enable compiler warnings (Codeblocks obviously does it for you).

C programming - "Undefined symbol referenced in file"

I am trying to write a program to approximate pi. It basically takes random points between 0.00 and 1.00 and compares them to the bound of a circle, and the ratio of points inside the circle to total points should approach pi (A very quick explanation, the specification goes in depth much more).
However, I am getting the following error when compiling with gcc:
Undefined first referenced
symbol in file
pow /var/tmp//cc6gSbfE.o
ld: fatal: symbol referencing errors. No output written to a.out
collect2: ld returned 1 exit status
What is happening with this? I've never seen this error before, and I don't know why it's coming up. Here is my code (though I haven't fully tested it since I can't get past the error):
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(void) {
float x, y;
float coordSquared;
float coordRoot;
float ratio;
int n;
int count;
int i;
printf("Enter number of points: ");
scanf("%d", &n);
srand(time(0));
for (i = 0; i < n; i++) {
x = rand();
y = rand();
coordSquared = pow(x, 2) + pow(y, 2);
coordRoot = pow(coordSquared, 0.5);
if ((x < coordRoot) && (y < coordRoot)) {
count++;
}
}
ratio = count / n;
ratio = ratio * 4;
printf("Pi is approximately %f", ratio);
return 0;
}
use -lm during compilation(or linking) to include math library.
Like this: gcc yourFile.c -o yourfile -lm
need to Link with -lm.
gcc test.c -o test -lm
The error is produced by the linker, ld. It is telling you that the symbol pow cannot be found (is undefined in all the object files handled by the linker). The solution is to include the library which includes the implementation of the pow() function, libm (m for math). [1] Add the -lm switch to your compiler command line invocation (after all the source file specifications) to do so, e.g.
gcc -o a.out source.c -lm
[1] Alternatively, you could have your own implementation of pow() in a separate translation unit or a library, but you would still have to tell the compiler/linker where to find it.

Resources