3

I'm newbie in c. I have written a function in c and passes the arguments to it but I got different answers as I expect.

The function is

void GridDim(float long1, float long2, float dx,
             float lat1, float lat2, float dy,
             float depth1, float depth2, float dh,
             int *m, int *n, int *k)
{
    *m = (int) ((long2-long1)/dx+1);
    *n = (int) ((lat2-lat1)/dy+1);
    *k = (int) ((depth2-depth1)/dh+1);
}

I used gcc to compile it: gcc -c GridDim.c

then I used the object file for a main file and compile that file

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
 int m,n,k;
 GridDim(20.0,30.0,0.1,10.0,15.0,0.1,5.0,20.0,5.0,&m,&n,&k);
 printf("m= %d\nn= %d\nk= %d\n", m, n, k);
 return 1;
}

gcc test.c -o test GridDim.o

but I did not get the correct answer. Does anybody know why?

the answer should be m=101 n=51 k=4 but I got m=1 n=1 k=-12

8
  • 7
    What should be the "correct answer"? Commented Sep 28, 2013 at 8:35
  • 1
    Just a wild guess but may you forgot the () around the d?+1 terms (e.g. (long2-long1)/(dx+1))? Commented Sep 28, 2013 at 8:38
  • I don't know what's your expected correct answer, however please notice that in C the type cast (int)some_value is actually floor(some_value) which means (int)0.9 == 0. Also please use 0 as the return value of main when it exits normally. Commented Sep 28, 2013 at 8:40
  • 1
    @PengyuCHEN, or EXIT_SUCCESS, which makes the intention more clear Commented Sep 28, 2013 at 8:44
  • 1
    I rolled the question back to revision three because the changes by LihO altered the source code and the reported observed results, thus changing the question to one not asked by the OP. The results reported by the OP are symptomatic of a missing function declaration, and LihO’s changes concealed that by merging the sources into one module. It is okay to edit for formatting. It is not okay to edit to change the question fundamentally. Commented Sep 28, 2013 at 9:33

4 Answers 4

4

You need to declare a function prototype for your GridDim function and include it in the file that contains your main method.

The reason is that unless the compiler has a function prototype that declares the types of the arguments it will promote all floats into doubles. At run-time your floats gets promoted into doubles and then passed to GridDim which reads the first half of the double and interprets it as a float. If you print the values inside GridDim you will see that the floats come in as corrupted values.

If you declare your method in a header file, e.g. GridDim.h:

#ifndef __GRID_DIM_DOT_H__
#define __GRID_DIM_DOT_H__

extern void GridDim(float long1, float long2, float dx, float lat1, float lat2, float dy, float depth1, float depth2, float dh, int* m, int* n, int* k);

#endif/*__GRID_DIM_DOT_H__*/

... and #include it in GridDim.c (to ensure that the definition matches the declaration):

#include <stdio.h>
#include "GridDim.h"

void GridDim(float long1, float long2, float dx,
             float lat1, float lat2, float dy,
             float depth1, float depth2, float dh,
             int *m, int *n, int *k)
{
    printf("long1 =  %10.6f\n", long1);
    printf("long2 =  %10.6f\n", long2);
    printf("dx =     %10.6f\n", dx);
    printf("lat1 =   %10.6f\n", lat1);
    printf("lat2 =   %10.6f\n", lat2);
    printf("long1 =  %10.6f\n", dy);
    printf("depth1 = %10.6f\n", depth1);
    printf("depth2 = %10.6f\n", depth2);
    printf("dh =     %10.6f\n", dh);

    *m = (int) ((long2-long1)/dx+1);
    *n = (int) ((lat2-lat1)/dy+1);
    *k = (int) ((depth2-depth1)/dh+1);
}

... and #include it in Main.c to ensure that the call matches the declaration:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "GridDim.h"

int main()
{
 int m,n,k;
 GridDim(20.0f,30.0f,0.1f,10.0f,15.0f,0.1f,5.0f,20.0f,5.0f,&m,&n,&k);
 printf("m= %d\nn= %d\nk= %d\n", m, n, k);
 return 1;
}

... then the arguments will be passed correctly to the GridDim function. I added some printf statements so that you can see this.

If you comment out the #include "GridDim.h" in Main.c then you will see what is happening in your current version of the code:

long1 =    0.000000
long2 =    0.000000
dx =      -0.000000
lat1 =     0.000000
lat2 =     0.000000
long1 =   -0.000000
depth1 =   0.000000
depth2 =   0.000000
dh =       0.000000
m= 1
n= 1
k= -2147483648

With the #include the output looks like this:

long1 =   20.000000
long2 =   30.000000
dx =       0.100000
lat1 =    10.000000
lat2 =    15.000000
long1 =    0.100000
depth1 =   5.000000
depth2 =  20.000000
dh =       5.000000
m= 101
n= 51
k= 4
Sign up to request clarification or add additional context in comments.

1 Comment

[+1] for header file usage. personally I like using header files.
3

You are missing declaration of function. Declare function before main()

void GridDim(float long1, float long2, float dx,
           float lat1, float lat2, float dy,
             float depth1, float depth2, float dh,
             int *m, int *n, int *k);

And compile both files at a time

gcc main.c griddim.c -o result
./result

12 Comments

I didn't see OP saying anything about undefined reference to GridDim FYI...he's able to compile and run
@P0W That's because you can use a function without a prototype in C, but I think, Gangadhar got the problem right. However, please add something on header file usage, Gangadhar, because putting forward declarations into other files than one header file that's also included by the file with the function definition for signature checking is a sure road to disaster (it's the reason I don't upvote yet).
@P0W: It is possible to compile and run with a missing function declaration in old C implementations, and this may produce the results the OP reports, as the arguments would not be passed properly with a missing declaration. This answer is the only one so far that explains the observed results.
@P0W: The results are implementation-dependent. In a C implementation that uses IEEE 754 with correct rounding, the results are 101, 51, and 4. Lower-quality C implementations may give different results. However, the fact that these results are correct, in the sense they are the results one would get with exact mathematics, is a bit of luck. When the source text 0.1 is converted to double, it is rounded up to a representable value. When 10 is divided by that value, the quotient is rounded down. The two errors happen to cancel, producing exactly 100, as desired.
@Gangadhar Well it's in section 6.5.2.2 Function calls of ISO/IEC 9899:201x: If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.
|
1

As I've said in the comments, the type cast (int) in C does trimming, e.g. (int)0.9 == 0. Here you may want to use the function round provided by math.h

The following code solves your problem.

#include <math.h>
void GridDim(float long1, float long2, float dx,
             float lat1, float lat2, float dy,
             float depth1, float depth2, float dh,
             int *m, int *n, int *k)
{
    *m = round((long2-long1)/dx+1);
    *n = round((lat2-lat1)/dy+1);
    *k = round((depth2-depth1)/dh+1);
}

EDITED:

Here's it's specified in the standard document, in Section 6 of ISO/IEC 9899:TC2:

6.3.1.4 Real floating and integer

1 When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.50)

AGAING EDITED:

However I have to say that your original code produces your-correct-answers on my machine. Please notice this problem is actually generated by the machine-dependent floating point number calculation:

Assuming your're calculating the product of 1.0 and 5.0: float x = 1.0 * 5.0. Here due to the precision, your actual result MAY BE 5.000002 or 4.999998, depends on the platform that runs this code.

So when it's 5.000002, your type cast (int)x works since it just cast the result to 5, however when it's 4.999998 it would be casted to 4. And round would always produce your expected answers.

AGAIN EDITED:

Oh sorry I'm not pointing the exact reason of your problem! You're missing the function definition. Here I vote for @Gangadhar's answer.

2 Comments

This does not explain the results reported in the problem, “m=1 n=1 k=-12”.
@EricPostpischil Yes that's true. Gangadhar's presenting the right answer I think.
0

before storing the value in the variable you have to take ceil value. because in c 0.9999 value if you are converting into the int it will convert to zero so before storing the value you have to take ceil value.

void GridDim(float long1, float long2, float dx,
             float lat1, float lat2, float dy,
             float depth1, float depth2, float dh,
             int *m, int *n, int *k)
{

   *m = (int) (ceil((long2-long1)/dx)+1);
   *n = (int) (ceil((lat2-lat1)/dy)+1);
   *k = (int) ((depth2-depth1)/dh+1);
}

5 Comments

This does not explain the results reported in the problem, “m=1 n=1 k=-12”.
I'm not a down voter but I will leave a comment because I think srihari is nearly right about an additional problem in the code. *m = (ceil((long2-long1))/dx); would produce a better answer where (long2 - long1) is exactly divisible by dx.
@richj: Even if floating-point were the problem here, ceil is the wrong solution. That could make the answers too large.
@EricPostpischil: I can see how it might make the grid too small, if we really don't mind creating an extra column of cells to hold entries on the far edge of the grid. Do you have a test case where the answer is too large please?
@richj: If you set the first three arguments to GridDim to 0.f, 210.f, 2.1f and compile and execute with a C implementation that uses IEEE 754 with correct rounding, the result for m with ceil is 102, where exact mathematics would produce 101. This is because the source text 2.1f is must be rounded down slightly when converted to a float, and the quotient 210.f / 2.1f yields a number slightly greater than 100, so ceil makes it 101 (and then the one is added, making 102).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.