Will the scope of floating point variables affect their values?

If we execute the following C# code on a console application, we will get a message as The sums are Not equal.

If we execute it after uncommenting the line System.Console.WriteLine(), we will get a message as The sums are equal.

    static void Main(string[] args)
    {
        float f = Sum(0.1f, 0.2f);
        float g = Sum(0.1f, 0.2f);

        //System.Console.WriteLine("f = " + f + " and g = " + g);

        if (f == g)
        {
            System.Console.WriteLine("The sums are equal");
        }
        else
        {
            System.Console.WriteLine("The sums are Not equal");
        }
    }

    static float Sum(float a, float b)
    {
        System.Console.WriteLine(a + b);
        return a + b;
    }

What is the actual reason for this behavior?

Jon Skeet
people
quotationmark

What is the actual reason for this behaviour?

I can't provide details for exactly what's going on in this specific case, but I understand the general problem, and why using Console.WriteLine can change things.

As we saw in your previous post, sometimes operations are performed on floating point types at a higher precision than the one specified in the variable type. For local variables, that can include how the value is stored in memory during the execution of a method.

I suspect that in your case:

  • the Sum method is being inlined (but see later)
  • the sum itself is being performed with greater precision than the 32-bit float you'd expect
  • the value of one of the variables (f say) is being stored in a high-precision register
    • for this variable, the "more precise" result is being stored directly
  • the value of the other variable (g) is being stored on the stack as a 32-bit value
    • for this variable, the "more precise" result is being reduced to 32 bits
  • when the comparison is performed, the variable on the stack is being promoted to a higher-precision value and compared with the other higher-precision value, and the difference is due to one of them having previously lost information and the other not

When you uncomment the Console.WriteLine statement, I'm guessing that (for whatever reason) forces both variables to be stored in their "proper" 32-bit precision, so they're both being treated the same way.

This hypothesis is all somewhat messed up by the fact that adding

[MethodImpl(MethodImplOptions.NoInlining)]

... does not change the result as far as I can see. I may be doing something else wrong along those lines though.

Really, we should look at the assembly code which is executing - I don't have the time to do that now, unfortunately.

people

See more on this question at Stackoverflow