3

I have a C# project configured for x86 platform target. The application works perfectly in WinXP, but it gets problem in Win7. I am using VS2008.

Please see the test code below (the problem: it prints 0 in WinXP and 1 in Win7).
Note: the code also works fine in Win7 if running on Debug mode or adding a line of trace.

Please advise, thanks!

using System;
using System.Windows.Forms;

namespace Hello
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            //The problem: it shows "0" in WinXP, and shows "1" in Win7
            MessageBox.Show(Test.GetValue().ToString());

            Environment.Exit(0);
        }
    }

    public class Test
    {
        static int a = 0;

        static public float GetValue()
        {
            float b = 0.8149883f;

            float x = (696f / b + a);

            //Note: it returns 0 if uncomments the line below, otherwise it returns 1 
            //MessageBox.Show("hello");

            return (x - (int)x);
        }
    }
}
tony
  • 31
  • 1
  • What kind of problem do you have on Windows 7? – PVitt Sep 08 '11 at 11:09
  • 2
    Floating points are not guaranteed to give the same result always. – CodesInChaos Sep 08 '11 at 11:10
  • How can it show `0` or `1`? it is a floating point number, and should be `0.0` or `1.0`. I suspect your provided code is not the same that produces this behavior. – leppie Sep 08 '11 at 11:16
  • 1
    @Code You'll get the same result everytime on the same hardware. – David Heffernan Sep 08 '11 at 11:39
  • @leppie `(1.0f).ToString()=="1"` – CodesInChaos Sep 08 '11 at 11:40
  • @David that may be the case in practice, but I doubt you're guaranteed that. If you think of a jitter that changes the code of hotspots (I think some java vms do that) the result might differ if during the execution of one program. Or does the .net specification guarantee that property somewhere? (unlikely IMO) – CodesInChaos Sep 08 '11 at 11:43
  • @Code I mean at the hardware level. The same opcodes with the same data will produce the same results on the same hardware. Put those opcodes and data on a different piece of hardware and you can get different results. – David Heffernan Sep 08 '11 at 11:45
  • Well since a .net assembly does contain intermediate codes and there is no guarantee that the machine code even stays the same over the lifetime of the program or between different runs of the program this it's irrelevant in practice that that on one set of hardware floating point operations written in native code will be the same all the time. (Assuming the control word is constant too) – CodesInChaos Sep 08 '11 at 11:49

3 Answers3

1

The JIT compiler is free to optimize floating point even if that slightly changes the result. You simply can't expect the results to be the same on all platforms.

Sometimes it interprets your 853.9999... as 854.0 sometimes it keeps it as 853.9999.... This is because on some platforms the intermediate results are calculated with higher precision. And at which point it gets degraded to float varies. In your example it looks like the message box forces the compiler to store the value outside of the FPU at which point it gets converted to 32 bit float.

Related question: Is floating-point math consistent in C#? Can it be?

If performance isn't too important you can simply use Decimal and get deterministic results.

Community
  • 1
  • 1
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Good reference, and is it points out, this is a representation issue of the intermediate result. The same issue occurs with a C++ float. It's really not a JIT issue - .NET is IEEE-754 compliant, and there isn't enough precision to capture the quotient. Using the abs(a-b) – holtavolt Sep 08 '11 at 13:53
1

In addition to the point that @CodeInChaos makes, floating point calculations vary on different hardware architectures. That's simply the nature of floating point units. Even two FP units that both adhere to IEEE754 can give different results to each other. Obviously the differences will be vanishingly small. So even without any JIT differences, you may see different results on a different machine.

In your case you simply need to compare floating point values in a different way. Instead of testing for exact equality, test for equality up to a small tolerance, i.e. abs(a-b)<epsilon where epsilon is a small number.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I think the simply operations(+ * - / ...) are fully specified in the IEEE standard. Some complicated operations such as sin/cos are not completely specified. – CodesInChaos Sep 08 '11 at 11:51
  • @CodeInChaos You could well be right. I'm mostly speaking from experience. In my line of work I test my app for regression by comparing floating point results exactly. This works so long as the tests are performed on the same hardware. There's no JITter to mess things up for me though! – David Heffernan Sep 08 '11 at 11:59
  • Without JITter floating point should behave the same as long as the hardware is the same and you set the control word to a known value. – CodesInChaos Sep 08 '11 at 12:32
0

Note that if you use double, the results work as expected.

If you take the result of the division and look at the representation in IEEE-754 for both float and double representation, you'll see why:

853.99998993850586566702859415282

As float: 854.00000

As double: 853.99998993850590

(From http://babbage.cs.qc.edu/courses/cs341/IEEE-754.html)

Why you are seeing different results between debug and release, I'm not sure - I don't see that in VS2010.

holtavolt
  • 4,378
  • 1
  • 26
  • 40
  • I wouldn't be surprised if slightly tuned values will exhibit exactly the same problem even with double. – CodesInChaos Sep 08 '11 at 11:38
  • In order to exhibit the problem with a double, you need to go beyond the 52 bits of precision in the significand. Using the page above, it's pretty easy to derive that the value 853.99999999999995 is the first value that gets rounded up to 854 using a IEEE-754 double (assuming default C++/.NET rounding modes.) – holtavolt Sep 08 '11 at 14:02