Kenneth Brody
3/18/2011 6:27:00 PM
On 3/18/2011 2:09 PM, lloyd wrote:
> I find this odd:
>
> ===========================
> #include<stdio.h>
>
> int main(int argc, char *argv[])
> {
> float phi, phi_start, phi_end, phi_step;
>
> phi_start = 0.0;
> phi_end = 3.0;
> phi_step = 0.2;
>
> for (phi = phi_start; phi<= phi_end; phi=phi+phi_step)
> printf("phi = %.15f\n",phi);
>
> return 0;
> }
> ============================
>
>> ./test
> phi = 0.000000000000000
> phi = 0.200000002980232
> phi = 0.400000005960464
[...]
> phi = 2.600000381469727
> phi = 2.800000429153442
>>
>
> What the heck is up? It's fine if I set phi_step to 0.25 (I mean you
> get the expected values of phi.) The bad digits are further away from
> the decimal point if I use doubles instead, but they still are enough
> to break a test of "if (phi==floor(phi))" which I want to use so I can
> flag cases where phi is an integer. Currently I'm using "if (phi -
> floor(phi)< epsilon)" to catch those cases, and I've had to change my
> loop condition to "phi<=phi_end+epsilon". What's the usual practice?
Just like there are some numbers, such as 1/3, which cannot be represented
exactly in decimal, there are numbers which cannot be represented exactly in
binary.
The number 1/5 (0.2 in decimal) is one such number. On your particular
implementation, the value given is approximately 0.200000002980232 instead
of exactly 0.2. The number 1/4 (0.25 in decimal) can be represented exactly
in binary, which is why you don't run into any issues here with it.
You can either continue using floating point along with an epsilon, or you
can use integer values for the loop control, if you are in charge of the values.
Perhaps, calculate the number of iterations that "should" be done with the
floating point numbers, and then use that as an integer counter. Perhaps
something like this (untested) calculation:
int iterations = floor( (phi_end-phi_start) / phi_step + epsilon );
--
Kenneth Brody