this is driving me CRAZY

Started by
11 comments, last by eq 18 years ago
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]
Author Freeworld3Dhttp://www.freeworld3d.org
Advertisement
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.
Mitchen Games
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.
Author Freeworld3Dhttp://www.freeworld3d.org
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).
The semi-colon was a typo in the forum post. It does not exist in my code, I fixed the above code fragment.
Author Freeworld3Dhttp://www.freeworld3d.org
Quote:Original post by soconne
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 !!


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:
Free Image Hosting at www.ImageShack.us (Click again on the image to make it bigger once more in ImageShack)

Hope that helps [smile]
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).
Quote:Original post by Doggan
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).


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)
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!
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

This topic is closed to new replies.

Advertisement