-1

Question is not about why 0.1 + 0.9 is not equals 1.0. Its about different behaviour of a equals.

Can someone explain why examples below works differently.

float q = 0.1f;
float w = 0.9f;
float summ = q + w;

q + w == 1.0f; // False
summ == 1.0f; // True

Why operator == works differently?

user854301
  • 5,383
  • 3
  • 28
  • 37
  • 1
    `summ == 1.0f` shouldn't be `false` as well? – Soner Gönül May 15 '15 at 07:24
  • [language-agnostic - Is floating point math broken?](http://stackoverflow.com/q/588004/995714) – phuclv May 15 '15 at 07:38
  • 1
    Lưu Vĩnh Phúc, the question is not about why `0.1+0.9 != 1`. It is about why I have different results for equals operator. – user854301 May 15 '15 at 08:02
  • It is all the same. Floating point arithmetic is just 'broken'. – Patrick Hofman May 15 '15 at 08:11
  • Patrick Hofman, The question "Why is floating point arithmetic in C# imprecise? " explains only `q + w == 1.0f; // False` but doesn't explain `summ == 1.0f; // True`/ – user854301 May 15 '15 at 08:14
  • The explanation is "you are lucky in #2". – Patrick Hofman May 15 '15 at 08:25
  • 1
    it doesn't matter what values are there `0.1+0.9` or `0.2+0.8` or `0.4 + 0.6` the result is one `q + w == 1.0f; // False` and `summ == 1.0f; // True`. For me "you are lucky in #2" is not an explanation, I prefer solid arguments. – user854301 May 15 '15 at 08:44
  • 1
    @user854301, have a read of the "Comparing floating point numbers" section at http://csharpindepth.com/articles/general/floatingpoint.aspx. Your question is valid and hasn't been answered by the "duplicate", but this link helps. It's all to do with how numbers are handled by the CPU, the JIT compiler, the debugger and .NET itself, which leads to the sort of inconsistencies you are seeing. The bottom line is: it's all a bit of a mess and so don't compare floats for equality. – David Arno May 15 '15 at 08:58
  • I´d say it`s random... the sum is calculated two times independently. See my theory as an answer below . Can you proof the result does not change when executing the program multiple times? – Marc Wittmann May 15 '15 at 10:19
  • 1
    This isn't a duplicate: it's a question about intermediate rounding. As I am not allowed to post an answer, I'll refer you here: https://msdn.microsoft.com/en-us/library/e7s85ffb.aspx – Simon Byrne May 16 '15 at 10:29
  • 1
    This example is biaised: `summ` will be evaluated at compile-time (assignment), whereas `q + w` will be evaluated at runtime. Presumably the compiler is able to make better floating point arithmetic. So @Patrick-Hofman was right, somehow you were lucky in the second case. – Kryptos May 16 '15 at 12:42
  • @Kryptos, variables could be initialized as `float q = float.Parse("0,1")` and result will be the same. @Rawling answer shows what happens under the hood. – user854301 May 18 '15 at 11:10

2 Answers2

2

Looking further into the IL:

summ is stored in a local of type float32

IL_000d:  ldloc.0
IL_000e:  ldloc.1
IL_000f:  add
IL_0010:  stloc.2

the result of q + w used in q + w == 1.0f is not; it is used directly in a comparison

IL_0011:  ldloc.0
IL_0012:  ldloc.1
IL_0013:  add
IL_0014:  ldc.r4     1.
IL_0019:  ceq

Presumably the storage of summ in a local means it loses enough precision that it becomes equal to 1.0f.

Rawling
  • 49,248
  • 7
  • 89
  • 127
  • 1
    As I can't post an answer (due to the duplicate block), the reason is that in `q + w == 1.0f`, the result of the addition is not being rounded to a `float`. As per the docs "... the compiler performs rounding on variables of type float to the correct precision for assignments and casts and when parameters are passed to a function" - https://msdn.microsoft.com/en-us/library/e7s85ffb.aspx – Simon Byrne May 16 '15 at 10:34
  • 1
    @SimonByrne Good point, it's always better to go via the spec than just what the result is. Feel free to post that as an answer, I disagree with the dupe close. – Rawling May 16 '15 at 11:40
  • This is just another reason that you should never compare two floating point numbers. Floating point numbers are supposed to be equals when they are "close enough" (situation dependent). – Kryptos May 16 '15 at 12:36
  • 2
    @Krypos There's nothing inherently wrong with comparing floating point numbers: you just have to be aware of issues such as intermediate precision. – Simon Byrne May 16 '15 at 21:18
2

The problem is due to the fact that the intermediate calculations are being performed in a higher precision, and the rules for when to round back to float precision are different in each case.

According to the docs

By default, in code for x86 architectures the compiler uses the coprocessor's 80-bit registers to hold the intermediate results of floating-point calculations.

... the compiler performs rounding on variables of type float to the correct precision for assignments and casts and when parameters are passed to a function"

float summ = q + w is an assignment, and hence is rounded to the nearest float, which in this case is 1.

q + w == 1.0f is neither a cast, assignment or function call, so the result of the addition is still an extended precision float, which is close, but not equal, to 1.

Community
  • 1
  • 1
Simon Byrne
  • 7,694
  • 1
  • 26
  • 50