4

In my program this fragment:

  trace.log(
   String.Format("a= {0:R} b= {1:R} a<b= {2}", 
    b.GetPixel(447, 517).GetBrightness(), (100F / 255F),
    b.GetPixel(447, 517).GetBrightness() < (100F / 255F))
  );

outputs this in Debug\prog.exe:

 a= 0.392156869 b= 0.392156869 a<b= False

but this different result in Release\prog.exe:

 a= 0.392156869 b= 0.392156869 a<b= True

Can anyone explain why the same operands give a different comparision result? And recommend a remedy, ideally program-wide such as a compiler switch? Thanks.

EDIT: Clarification: the above results are from launching Debug\prog.exe and Release\prog.exe in Windows Explorer.

EDIT: Further info: executing from VS, "Start Debugging" gives False (i.e. the accurate result, same as WE-launched Debug\prog.exe), and "Start without debugging" gives True (i.e. the inaccurate result, same as WE-launched Release\prog.exe.

EDIT: Alternative test cases having literals substituted

These two cases

  trace.log(
   String.Format("a= {0:R} b= {1:R} a<b= {2}",
    0.392156869F, 0.392156869F, 
    0.392156869F < 0.392156869F)
  );
  trace.log(
   String.Format("a= {0:R} b= {1:R} a<b= {2}",
    0.392156869F, (100F / 255F),
    0.392156869F < (100F / 255F))
  );

show no discrepancy in Debug and Release output. This case:

  trace.log(
   String.Format("a= {0:R} b= {1:R} a<b= {2}",
    b.GetPixel(447, 517).GetBrightness(), 0.392156869F,
    b.GetPixel(447, 517).GetBrightness() < 0.392156869F)
  );

shows the same discrepancy (and inaccuracy in Release) as the original test case.

EDIT: Belated minimal test case demoing the problem

Color c = Color.FromArgb( 255, 100, 100, 100 );
trace.log(
 String.Format("a= {0} b= {1} a<b= {2}",
 c.GetBrightness(), 0.392156869F,
 c.GetBrightness() < 0.392156869F)
);

outputs this correct result in Debug\prog.exe:

 a= 0.392156869 b= 0.392156869 a<b= False

but this incorrect result in Release\prog.exe:

 a= 0.392156869 b= 0.392156869 a<b= True

EDIT: Remedies

1) From Peter's answer below:

trace.log(
 String.Format("a= {0:R} b= {1:R} a<b= {2}",
  c.GetBrightness(), 0.392156869F, 
  c.GetBrightness().CompareTo(0.392156869F)<0)
);

2) From ChrisJJ, the questioner (UPDATED):

float comp = c.GetBrightness();
trace.log(
 String.Format("a= {0:R} b= {1:R} a<b= {2}", 
 comp, 0.392156869F,
 comp < 0.392156869F)
);

I think this adds to the evidence for a Release-mode compiler bug.

ChrisJJ
  • 2,191
  • 1
  • 25
  • 38
  • What does the value of GetPixel return exactly? I would like to test this myself and I suspect that this is the result of rounding between Debug and Release when calculating the brightness in GetBrightness. – Peter O. Aug 17 '11 at 23:09
  • It returns Color.FromArgb( 255, 100, 100, 100 ) and though I though I'd found this substitution failed to show the problem, I now find in fact it does, so I have added this case to the question. Thanks P. – ChrisJJ Aug 18 '11 at 18:37

1 Answers1

7

It's most likely the result of different floating point rules between debug mode and release mode (see here and here).

Update:

Thanks to your new update to the question, I was able to reproduce the problem on my machine (64-bit Windows). This appears to happen only when setting the platform to X86; the X64 and AnyCPU platforms show the correct result in release mode. Probably, when the platform is X86, the common language runtime applies X86 emulation in 64-bit machines and apparently messed up in the comparison operator.

However, I found a possible workaround: Use CompareTo instead of the "<" and ">" operators, like this:

c.GetBrightness().CompareTo(0.392156869F)<0

On my machine, this will provide the same correct results in X86 as in X64 and AnyCPU.

Community
  • 1
  • 1
Peter O.
  • 32,158
  • 14
  • 82
  • 96
  • Thanks for those articles Peter, but I don't actually see there any floating point rule that invalidates the Language Specifications' definition of the < operator and hence supports the Release case's non-compliance with that definition. – ChrisJJ Aug 17 '11 at 20:24
  • Responmding to your edit. "Any of these operations, especially the divisions, might be the cause of different rounding results between Debug and Release Modes." Is this not disproved by the fact that Format R shows the expression's numeric value is identical Debug v. Release? – ChrisJJ Aug 18 '11 at 18:19
  • Responding to your update. .CompareTo does solve it here. Another solution is to wrap the "<" in a function. ISTM this indicates a ratehr poor optimiser! Thanks again Peter. – ChrisJJ Aug 20 '11 at 16:33