Floating Point, Why are you so buoyant?

Floating point precision can often be confusing to people.  The following code block should illustrate some important things to keep in mind about floating point numbers. In this block I am attempting to get the value 64,000,000 in two different ways.

float v = 400400400;
float w = 0;

for (int i = 0; i < 400; i++)
{
    for (int j = 0; j < 400; j++)
    {
        for (int k = 0; k < 400; k++)
        {
            w += 1;
        }
    }
}

Console.WriteLine(“v: “ + v);
Console.WriteLine(“w: “ + w);

On line one I am put 400*400*400 directly into the float v.  Then I declared a float w.  I have w incremented 64,000,000 times.  Once this process is done I print out the numbers.  These are the results you get:

v: 6.4E+07
w: 1.677722E+07

The value for v is correct but why is w not equal to v? The first instinct many might have when a number stored in a variable is wrong is that it must be an overflow.  However, v is able to display the correct value and its the same type as w.  The reason v and w are different is because of floating point precision.  A float is able to represent the number 64,000,000 but when the program attempts to get to this value by incrementing it eventually hits a road block and the variable won’t change anymore.  This occurs when w becomes the values 1.677722 x 10^7.  At this point w has used up all of its mantissa (or significant digit portion) and can no longer represent the next number.   As a you store larger and larger value in a floating point number its precisions (how often it will have n+1 if it has n) decreases. (For more information about floating point numbers see Floating Point Numbers.)

CHALLENGE:

If you run the above program with optimizations on you will always get the correct answer.  My question to everyone who reads this (basically me :) ) is how are optimizations making this work differently.