# this is driving me CRAZY

This topic is 4305 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

This is driving me nuts, somebody please tell me I'm over looking something. For some reason the following code outputs: i=0.95 i=1.0 i=1.0
    float j = 0;
float i = 0;
for(j = 0; j < 1; j += 1/20.0f)
i = j;
printf("i=%f\n", i);

for(j=0; j < 1; j += 1/40.0f)
i = j;
printf("i=%f\n", i);

j = 0;
while(j < 1)
{
i = j;
j += 1/40.0f;
}


WHY IS THIS HAPPENING!! The 2nd for loop should NOT print out i = 1.0, it should print out i = 0.975 !!! And the 3rd while loop should print out 0.975 NOT 1.0 !! ----------------------------- *****EDIT********** ----------------------------- I just tried the following code
    for(j=0; j < 1; j += 1/39.9999f)
i = j;
printf("i=%f\n", i);

for(j=0; j < 1; j += 1/40.0f)
i = j;
printf("i=%f\n", i);

for(j=0; j < 1; j += 1/40.1f)
i = j;
printf("i=%f\n", i);


And this is what I got i=0.975002 i=1.000000 i=0.997506 [Edited by - soconne on April 5, 2006 11:51:11 PM]

##### Share on other sites
wow, thats weird. I have no idea. I agree, it should print out 0.975. Maybe it has something to do with your printf line, b/c I'm not exactly sure on the syntax of that function.

##### Share on other sites
The printf statement is correct. I also used cout with the same results.

I'm running this on a P4 M 2.0Ghz.

Also, if I switch the 1/40.0f with 1/39.9999f it works just fine. Its only when I do exactly 1/40.0f that it gives me this problem.

##### Share on other sites
for(j=0; j < 1; j += 1/40.0f);

That can't be good? (hint: semi-colon at end)

The code does what you tell it to do.

- After the first for loop, i = .95, j = 1.00.
- The 2nd for loop is taking j += 1/40. This is j = 1.00 + .025 = 1.025
- Now you're setting i = j = 1.025

I'm not even going to look at the third loop as your error probably stems from this. Try stepping through your code with a paper and pencil. It all 'makes sense' when you take it one step at a time (and indent properly).

##### Share on other sites
The semi-colon was a typo in the forum post. It does not exist in my code, I fixed the above code fragment.

##### Share on other sites
Quote:
 Original post by soconneWHY IS THIS HAPPENING!! The 2nd for loop should NOT print out i = 1.0, it should print out i = 0.975 !!! And the 3rd while loop should print out 0.975 NOT 1.0 !!

Floating point errors. Use double instead of float for your datatypes and it should work correctly (it did for me when I made that change).

Here's a screenshot showing what I am talking about:
(Click again on the image to make it bigger once more in ImageShack)

Hope that helps [smile]

##### Share on other sites
I think you need to re-read the for loop chapter of your C++ book. Given for(j=0; j < 1; j += 1/40.0f):

1). j is initialized to 0
2). is j < 1? If yes, enter loop. If no, exit loop.
3). execute loop
4). increment j (j += 1 / 20.0f)
5). is j < 1? If yes, enter loop. If no, exit loop.
6). repeat from 3).

In this case, after so many iterations, since j is a float, it is really .99999. It assigns this value to i. It then incrmenets j, j += 1 / 20.0f, which is j = .999999 + .025 = 1.025. The condition, j < 1, is not met. Therefore your loop is not entered, and i remains .999999 (or 1.0).

##### Share on other sites
Quote:
 Original post by DogganI think you need to re-read the for loop chapter of your C++ book. Given for(j=0; j < 1; j += 1/40.0f):1). j is initialized to 02). is j < 1? If yes, enter loop. If no, exit loop.3). execute loop4). increment j (j += 1 / 20.0f)5). is j < 1? If yes, enter loop. If no, exit loop.6). repeat from 3).In this case, after so many iterations, since j is a float, it is really .99999. It assigns this value to i. It then incrmenets j, j += 1 / 20.0f, which is j = .999999 + .025 = 1.025. The condition, j < 1, is not met. Therefore your loop is not entered, and i remains .999999 (or 1.0).

I haven't looked though your previous two posts, but I just want to let you know that running the program using floats I get
i=0.950000i=1.000000i=1.000000Press any key to continue

But when I change those floats to doubles, and do not change any other code I get:
i=0.950000i=0.975000i=0.975000Press any key to continue

Which is what the OP was saying in the first post. Just throwing that out there - I am not looking at logics or anything like that, just final outputs. Here's the source code that you can test for yourself too if you are interested.
#include <iostream>int main(int argc, char* argv[]){	{	printf("First using floats:\n");	float j = 0;    float i = 0;    for(j = 0; j < 1; j += 1/20.0f)	i = j;    printf("i=%f\n", i);    for(j=0; j < 1; j += 1/40.0f)            i = j;    printf("i=%f\n", i);    j = 0;    while(j < 1)    {	i = j;	j += 1/40.0f;    }	printf("i=%f\n", i);	}	printf("\n\n");	{	printf("Now using doubles:\n");	double j = 0;    double i = 0;    for(j = 0; j < 1; j += 1/20.0f)	i = j;    printf("i=%f\n", i);    for(j=0; j < 1; j += 1/40.0f)            i = j;    printf("i=%f\n", i);    j = 0;    while(j < 1)    {	i = j;	j += 1/40.0f;    }	printf("i=%f\n", i);	}	return 0;}

I think the problem was from using floats, but if someone can rewrite the code a different way that does the same thing using floats, and it does produce the correct output, meaning these logics are wrong, then I'd like to see that (I don't have any more time hehe)

##### Share on other sites
The real answer is a little complicated.

In fact, if you compile in release mode, you'll find that it suddenly becomes correct again! There are a couple things going on here... first 1/40.0f is not exactly representable by IEEE 754 floating point. The best approximation is 3ccccccd which equals 0.02500000037252903.

Why debug is wrong: In debug mode, the compiler saves the results of its computations out to the stack after every operation. This keeps values in memory coherent with values in registers, so it is generally useful. However, in release, it'll just accumulate additions to a floating point register which is internally much higher precision (80-bit maybe?). In debug, by saving and reloading from the stack, it's blowing away all that extra precision.

However, none of this explains why it would go one iteration too many! (the fact that 3ccccccd is *more* than 1/40.0 exactly means that it should never stop too late). So what really happens is that IEEE 754 has a "rounding mode" such that it will oscillate back and forth roughly around the actual value (N * 3ccccccd). When using just 32-bits of precision in debug mode, sometimes this error will drop far enough under the actual value that it appears *less*!

For instance, instead of testing against "while(j < 1)" try "while(j < .575f)"... now try "while(j < .60f)". See something funny? That's the first point of inflection, where it becomes wrong. Now try (j < 2.325f)... still wrong. What about (j < 2.35f) ?

The fact that it rounds funny and gives imprecise results is definitely a good sign to not rely on floating point precision... The fact that release and debug modes give different results should be a Huge red flag!

##### Share on other sites
Quote:
 Original post by ajas95...

Great explanation, I was just in IRC talking this over with some people and that's a great detailed post covering the basics of what was said! I was going to mention something, but you got it all down [smile] You do learn something new everyday

##### Share on other sites
Never use floating point arithmetic in loops, the errors add up very quickly, and you can't predict how many iterations there will be. Always use integer arithmetic for the loops!
for(int i = 0; i < 40; i++){    float f = i / 40.0f;    // now do something with f}

##### Share on other sites
i agree with you Fred304.
I also had problems with that in the past.
just never use floats or doubles in loops.

##### Share on other sites
You always need to be really aware of the floating point rounding errors etc.
Your loop could easily be turned into an infinite loop that makes your application hung.
How?, if you increment the counter with a very small value and then have the exit condition set to a very large one, something like:

for (float f = 100000000.0f; f < 1000000000.0f; f += 0.000000001f)

The problem here is that a very small value can't be added to a very large value, the large value will not changed at all (due to the internal representation)!
This is also compiler / optimization dependent, so it might work perfect in debug mode (or release) but not in some builds = very painful to find.
So be very careful when using float/double as termination conditions in loops etc.

Disclaimer: I haven't actually tested the loop with the above values, but in principle it works like that.