Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double equals 0 problem in C

I was implementing an algorithm to calculate natural logs in C.

double taylor_ln(int z) {
    double sum = 0.0;
    double tmp = 1.0;

    int i = 1;
    while(tmp != 0.0) {
        tmp = (1.0 / i) * (pow(((z - 1.0) / (z + 1.0)), i));
        printf("(1.0 / %d) * (pow(((%d - 1.0) / (%d + 1.0)), %d)) = %f\n", i, z, z, i, tmp);
        sum += tmp;
        i += 2;
    }

    return sum * 2;
}

As shown by the print statement, tmp does equal 0.0 eventually, however, the loop continues. What could be causing this?

I am on Fedora 14 amd64 and compiling with:

clang -lm -o taylor_ln taylor_ln.c

Example:

$ ./taylor_ln 2
(1.0 / 1) * (pow(((2 - 1.0) / (2 + 1.0)), 1)) = 0.333333
(1.0 / 3) * (pow(((2 - 1.0) / (2 + 1.0)), 3)) = 0.012346
(1.0 / 5) * (pow(((2 - 1.0) / (2 + 1.0)), 5)) = 0.000823
(1.0 / 7) * (pow(((2 - 1.0) / (2 + 1.0)), 7)) = 0.000065
(1.0 / 9) * (pow(((2 - 1.0) / (2 + 1.0)), 9)) = 0.000006
(1.0 / 11) * (pow(((2 - 1.0) / (2 + 1.0)), 11)) = 0.000001
(1.0 / 13) * (pow(((2 - 1.0) / (2 + 1.0)), 13)) = 0.000000
(1.0 / 15) * (pow(((2 - 1.0) / (2 + 1.0)), 15)) = 0.000000
(1.0 / 17) * (pow(((2 - 1.0) / (2 + 1.0)), 17)) = 0.000000
(1.0 / 19) * (pow(((2 - 1.0) / (2 + 1.0)), 19)) = 0.000000
(1.0 / 21) * (pow(((2 - 1.0) / (2 + 1.0)), 21)) = 0.000000
and so on...
like image 934
mbreedlove Avatar asked Feb 01 '11 03:02

mbreedlove


2 Answers

The floating point comparison is exact, so 10^-10 is not the same as 0.0.

Basically, you should be comparing against some tolerable difference, say 10^-7 based on the number of decimals you're writing out, that can be accomplished as:

while(fabs(tmp) > 10e-7)
like image 72
Mark Elliot Avatar answered Oct 21 '22 22:10

Mark Elliot


Don't use exact equality operations when dealing with floating point numbers. Although your number may look like 0, it likely to be something like 0.00000000000000000000001.

You'll see this if you use %.50f instead of %f in your format strings. The latter uses a sensible default for decimal places (6 in your case) but the former explicitly states you want a lot.

For safety, use a delta to check if it's close enough, such as:

if (fabs (val) < 0.0001) {
    // close enough.
}

Obviously, the delta depends entirely on your needs. If you're talking money, 10-5 may be plenty. If you're a physicist, you should probably choose a smaller value.

Of course, if you're a mathematician, no inaccuracy is small enough :-)

like image 35
paxdiablo Avatar answered Oct 21 '22 23:10

paxdiablo