G
Gustavo Rondina
Hello CLC.
The C99 standard requires several of the usual math functions to have
both float and long double variants, such as sqrtf, cosf, sinf and so on.
What is the rationale behind the existence of those float variants? For
years programmers have used the usual C89 functions (i.e., double only)
even when working with floats, so why has C99 introduced those variants?
Are there any advantages?
I would guess that using a float specific function would allow the
implementation to take advantage of the some particular single precision
hardware and thus make the function faster, but this is just speculation
from my part.
Regarding the correctness, I suspect that both the regular double
functions and the float variants should return the same results no matter
what, but is it possible to guarantee that? I mean, since the usual C89
functions will perform the operations in double precision and then cast
the result to single precision if one is working with floats, how can one
be sure that the casts to double won't mess with the bits of the input
and output values?
In order to try to answer my doubts concerning this topic I've written
two small programs, which follow.
$ cat test1.c
#include <assert.h>
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
const unsigned int n = 10000000;
unsigned int i;
float a, b, c;
a = 3.40282347e+38F; /* max 32 bit float */
for (i = 0; i < n; ++i) {
b = sqrt(a);
c = sqrtf(a);
assert(b == c);
a = b;
}
return 0;
}
This one just checks if the results of both sqrt and sqrtf are exactly
the same using the == operator. I know comparing floats for equality like
this is dangerous, but in this case it does precisely what I want, which
is checking if the results of both functions are exactly the same down to
the last bit.
Compiling and running the program everything seems ok:
$ gcc -Wall -pedantic -std=c99 -o test1 test1.c -lm
$ ./test1
$
So I assume both functions will return the same, but I don't know if it
is just a coincidence on my implementation or if it is always the case.
The second program is a naive way of checking whether there are any
differences between the execution time of both functions.
$ cat test2.c
#include <assert.h>
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
const unsigned int n = 200000000;
unsigned int i;
float a, b;
assert(argc == 2);
a = 1.618033987F;
if (*argv[1] == 'f') {
printf("using sqrtf\n");
for (i = 0; i < n; ++i)
b = sqrtf(a);
} else {
printf("using sqrt\n");
for (i = 0; i < n; ++i)
b = sqrt(a);
}
return 0;
}
Compiling and timing the executions:
$ gcc -Wall -pedantic -std=c99 -o test2 test2.c -lm
$ time ./test2 f
using sqrtf
real 0m9.039s
user 0m8.806s
sys 0m0.050s
$ time ./test2 d
using sqrt
real 0m8.656s
user 0m8.459s
sys 0m0.010s
The double version seems to be a little bit faster, but I don't know
exactly why. Again, this probably is just on my implementation, but show
that sqrt and sqrtf may be implemented in different ways, even though
they yield the same results. Is that correct?
I don't know if I made myself clear, but I hope any of you guys can shed
some light on this.
Cheers,
The C99 standard requires several of the usual math functions to have
both float and long double variants, such as sqrtf, cosf, sinf and so on.
What is the rationale behind the existence of those float variants? For
years programmers have used the usual C89 functions (i.e., double only)
even when working with floats, so why has C99 introduced those variants?
Are there any advantages?
I would guess that using a float specific function would allow the
implementation to take advantage of the some particular single precision
hardware and thus make the function faster, but this is just speculation
from my part.
Regarding the correctness, I suspect that both the regular double
functions and the float variants should return the same results no matter
what, but is it possible to guarantee that? I mean, since the usual C89
functions will perform the operations in double precision and then cast
the result to single precision if one is working with floats, how can one
be sure that the casts to double won't mess with the bits of the input
and output values?
In order to try to answer my doubts concerning this topic I've written
two small programs, which follow.
$ cat test1.c
#include <assert.h>
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
const unsigned int n = 10000000;
unsigned int i;
float a, b, c;
a = 3.40282347e+38F; /* max 32 bit float */
for (i = 0; i < n; ++i) {
b = sqrt(a);
c = sqrtf(a);
assert(b == c);
a = b;
}
return 0;
}
This one just checks if the results of both sqrt and sqrtf are exactly
the same using the == operator. I know comparing floats for equality like
this is dangerous, but in this case it does precisely what I want, which
is checking if the results of both functions are exactly the same down to
the last bit.
Compiling and running the program everything seems ok:
$ gcc -Wall -pedantic -std=c99 -o test1 test1.c -lm
$ ./test1
$
So I assume both functions will return the same, but I don't know if it
is just a coincidence on my implementation or if it is always the case.
The second program is a naive way of checking whether there are any
differences between the execution time of both functions.
$ cat test2.c
#include <assert.h>
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
const unsigned int n = 200000000;
unsigned int i;
float a, b;
assert(argc == 2);
a = 1.618033987F;
if (*argv[1] == 'f') {
printf("using sqrtf\n");
for (i = 0; i < n; ++i)
b = sqrtf(a);
} else {
printf("using sqrt\n");
for (i = 0; i < n; ++i)
b = sqrt(a);
}
return 0;
}
Compiling and timing the executions:
$ gcc -Wall -pedantic -std=c99 -o test2 test2.c -lm
$ time ./test2 f
using sqrtf
real 0m9.039s
user 0m8.806s
sys 0m0.050s
$ time ./test2 d
using sqrt
real 0m8.656s
user 0m8.459s
sys 0m0.010s
The double version seems to be a little bit faster, but I don't know
exactly why. Again, this probably is just on my implementation, but show
that sqrt and sqrtf may be implemented in different ways, even though
they yield the same results. Is that correct?
I don't know if I made myself clear, but I hope any of you guys can shed
some light on this.
Cheers,