tags:

views:

2197

answers:

7

I'm getting unexpected results from the round() and roundf() functions in the math.h library. Here is the sample code:

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

int main(void)
{
    float f;
    double d;

    /* Note man page says that roundf() returns a float
       and round() returns a double */
    f = roundf(1.2);
    d = round(1.2);

    printf("%f\n", f);
    printf("%lf\n", d);

    return 0;
}

When I complie and run the program I get:

gcc -lm round.c
./a.out
288.000000
524288.000000

Wierd huh?

Update: Along with the answers I've confirmed that the code works properly on a newer complier. (I think %lf is not the correct printf specifier but that doesn't affect the end result in this case.) I'll need to figure out why may compiler is behaving this way because I have running code that uses round() and has been compiler on the same machine. I'll update the post when I figure it out.

gcc -v
Reading specs from /usr/lib/gcc-lib/i386-slackware-linux/2.95.3/specs
gcc version 2.95.3 20010315 (release)
+1  A: 

When I compile and run this exact code (under gcc on cygwin) I get:

$ ./a.exe
1.000000
1.000000

How new is your compiler? A compiler bug is all I can think of, as gcc -Wall gives no warnings either.

To add some further information this forum thread seems to show changing compiler to a newer version fixes it. If this doesn't work for you you'll need to give more details of compiler and OS, but given this seems to work for other people on three different platforms looks like your compiler is at fault.

Nick Fortescue
I did get it working on a newer complier. Due to my circumstances I can upgrade the complier.
Harry
Hooray - can I have accepted answer then?
Nick Fortescue
Or even an upvote?
Nick Fortescue
Upvoted everyone :-)
Harry
+1  A: 

This obviously doesn't help but the code looks and runs as expected for me. Everything seems fine.

vinc456
+1  A: 

To add to the chorus of approvals, this works fine for me (OS X Leopard using gcc).

Out of curiosity, do you need to use the -lm flag? I didn't the first time, and I did the second time, and there was no difference, but that's because libm.dylib and libc.dylib are symbolic links to the same library on OS X. Perhaps your libm is broken or something? Do you need to link to libm or not? I would think that the math.h functions would be part of libc...?

EDIT:

Before you do any of that, try using this instead:

float f = 0;
double d = 0;

I don't think that should change anything, but if it does, I win.

Chris Lutz
I need to specify it on Linux otherwise the compiler complains with "undefined reference to round".
Harry
Technically, the linker complains, not the compiler, but I understand. I added a new solution that MIGHT work, but probably not. Try it for good measure, but otherwise I think your libm is broken. You'll need to compile a new one.
Chris Lutz
+1  A: 

One more "works on my machine" on Ubuntu with gcc version 4.3.2.

$ ./a.out
1.000000
1.000000

I do get a couple of warnings when I compile, though.

$ gcc -lm round.c
round.c: In function ‘main’:
round.c:11: warning: incompatible implicit declaration of built-in function ‘roundf’
round.c:12: warning: incompatible implicit declaration of built-in function ‘round’
Bill the Lizard
Did you forget to #include <math.h>? Or perhaps your round() and roundf() functions are stored in another header (shouldn't be the case)?
Chris Lutz
I did #include <math.h>, so I'm not sure what the warnings are about.
Bill the Lizard
A: 

Looks like you're using it right, describe your environment some?

wybiral
+6  A: 

You can fail if you compile the code without telling gcc to compile with C99 mode (-std=c99) and tell it not to know about "special builtin" functions (using -fno-builtin). Then it assumes that your round/roundf function is defined as

int round();
int roundf();

Because in pre-C99 times there were no such functions yet, so it does not have a declaration and implicitly declares them then. And this will obviously fail, because the call-side treats the return value as an int, while the callee side (in the definition of those functions in the linked library) returns a float. I get these results for example:

[js@HOST2 cpp]$ ./a.out
1065353216.000000
-1048576.000000
[js@HOST2 cpp]$

Not that you think now that you could cast the return value to a float and be OK. Well, it's worse. The return value is not even guaranteed to have anything to do with the float returned. The call-side reads from a place that it knows where integers are returned. But your compiler may return floats in another place (say, in a floating pointer register) from the callee side. The above could actually have done anything, including aborting the program because it behaves in an undefined manner.

So, what can you do to make it work? Pass the compiler the std=c99 flag or use other ways to round (floor is one of them) which do not require those functions

gcc -std=c99 test.c -lm

See the manpage of man 3 round. However, if you have a very old GCC - i'm not sure whether it supports enough of C99 to have that switch. Look into the feature test macros described in the manpage of round then.

Johannes Schaub - litb
To clarify, what args did you use with gcc?
Harry
gcc -lm -fno-builtin test.c
Johannes Schaub - litb
as you say you use an older gcc, it may not support knowing about special builtin functions yet (treating for example calls to memcpy specially). so you wouldn't need the -fno-builtin to make it fail :)
Johannes Schaub - litb
Your're right. I added #define _ISOC99_SOURCE and it worked.
Harry
For me (Ubuntu Hardy, gcc 4.2.4), I get "102.000000-1048576.000000" with gcc -fno-builtin -lm or gcc -std=c89 -fno-builtin -lm, but gcc -std=c99 weird_round.c -fno-builtin -lm is correct (1.000000). So, litb's advice is sound.
Matthew Flaschen
+1  A: 

litb was on the right track. -std=c99 didn't work but adding #define _ISOC99_SOURCE worked. So the code looks like:

#define _ISOC99_SOURCE
#include <stdio.h>
#include <math.h>

int main(void)
{
...
Harry