Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
167 views
Welcome To Ask or Share your Answers For Others

1 Answer

It's not related to scope. It's the combination of the stack dynamics and floating point handling. Some knowledge of compilers will help make this counterintuitive behavior clear.

When the Console.WriteLine is commented, the values f and g are on the evaluation stack and stay there until after you've passed the equality test in your Main method.

When Console.Writeline is not commented, the values f and g are moved from the evaluation stack to the call stack at the moment of the invocation, to be restored to the evaluation stack when Console.WriteLine returns. And your comparison if (f == g) is done afterwards. Some rounding can occur during this storing of values to the call stack and some information can be lost.

In the scenario where you do invoke Console.WriteLine, the f and the g in the comparison test are not the same values. They've been copied and restored to a format that has different rules on precision and rounding, by the virtual machine.

In your particular code, when the invocation of Console.WriteLine is commented, the evaluation stack is never stored to the call stack and no rounding occurs. Because it is permitted for implementations of the platform to provide improved precision on the evaluation stack, this discrepancy can arise.

EDIT What we're hitting in this case is allowed by the CLI specification. In section I.12.1.3 it reads:

Storage locations for floating-point numbers (statics, array elements, and fields of classes) are of fixed size. The supported storage sizes are float32 and float64. Everywhere else (on the evaluation stack, as arguments, as return types, and as local variables) floating-point numbers are represented using an internal floating-point type. In each such instance, the nominal type of the variable or expression is either float32or float64, but its value can be represented internally with additional range and/or precision. The size of the internal floating-point representation is implementation-dependent, can vary, and shall have precision at least as great as that of the variable or expression being represented.

The keywords from this quote are "implementation-dependent" and "can vary". In the OP's case, we see his implementation does indeed vary.

Non-strictfp floating point arithmetic in the Java platform also has a related issue, for more info check also my answer to Will floating point operations on the JVM give the same results on all platforms?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...