Search Unity

Me falling into floating point error trap.

Discussion in 'Editor & General Support' started by Antypodish, Mar 9, 2019.

  1. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Generally I am aware about floating point challenges.
    But here is my recent annoying experience, from merely innocent math equation, which I would like to share with, if you care :)

    Btw. There is no question in this thread.

    Code (CSharp):
    1. int index = math.round ( ( scale / 2 ) + originOffset + 0.5f ) ; // Expecting = 1
    2. index -- ; // Expecting = 0
    It worked well ... until it didn't.
    Well, I had just few rare cases (once / twice a day, if code has been edited), when error occurred, but I was unable to replicate them. Until I did some further more dramatic changes recently and my index start returning negative values. It always should be int, none negative.

    Issue breakdown
    So what happened in this particular example, was following:
    scale = 2
    originOffset = -1
    index = -1 (unexpected result, while should be 0)


    Digging into the issue further, I extracted scale / 2, to debug it.

    Code (CSharp):
    1. float scaleHalve = scale / 2 ; // Meant to be = 1
    2. int index = math.round ( scaleHalve + originOffset + 0.5f ) ; // = 0?
    3. index -- ; // = -1?
    So what happens here was, that in some cases, scale / 2, returned 0.99...f
    That is near 1, but enough, to break whole series of issues in my case.
    Hence:

    scale = 2
    scaleHalve = 0.99...f
    originOffset = -1
    index = -1 (unexpected result, while should be 0)


    That gives:
    Code (CSharp):
    1. int index = math.round ( 0.99...f + (-1) + 0.5f ) ; // = 0?
    2. index -- ; // = -1? (Expecting = 0)
    therefore:
    Code (CSharp):
    1. int index = math.round ( 0.499...f ) ; // = 0?
    2. index -- ; // = -1?( Expecting = 0)
    After rounding:
    Code (CSharp):
    1. int index = 0 ;
    2. index -- ; // = -1? (Expecting = 0)


    My conclusion
    was, that I need ensure, that scaleHalve for that case is 1. Either rounding, or + 0.01f is sufficient.
    Hence:
    scale = 2
    scaleHalve = 1 (or 1.099..f
    originOffset = -1
    index = 0 (result)


    That gives:
    Code (CSharp):
    1. int index = math.round ( 1.099..f + (-1) + 0.5f ) ; // = 1 (as expected)
    2. index -- ; // = 0 (As expected)
    therefore:
    Code (CSharp):
    1. int index = math.round ( 0.5099...f ) ; // = 1(as expected)
    2. index -- ; // = 0 (As expected)
    Now it works.
     
    Last edited: Jun 9, 2019
    Deleted User and Kiwasi like this.
  2. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    I worry when using non-powers of two in equations as epsilons as their behavior is difficult to predict as the numbers that they are added to get larger and they are rounded to new values depending on the input value magnitude.
    I am not sure if I understand what your equation is, but can it not be written as:
    index = (int)(scale*0.5f + 0.25f).
    Assuming that you want to map 0,1 to 0, 2,3 to 1, etc with rounding of numbers in between to the nearest?
    Things are a little more tricky if you want truncation because of the rounding rules differ for even/odd numbers
     
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Fortunately did't had issue since my last/first post.

    But, regarding proposed index equation, in your example you got missing originOffset, which can be negative and positive values, with 0.5 resolution. Like for example -4.5, -2, 0, 0.5, 5, 7,5 etc. Of course I didn't described that in first post, since it wasn't relevant. Just picked faulty condition. Yet adding 0.25 rather 0.5 in full equation, changes condition, when index become 0, 1, 2, ... etc. If that makes sense.
     
    Last edited: Jun 10, 2019
  4. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    I used 0.25 to handle numbers like -0.5 consistently.
    If you have negative numbers you have to modify the equation as follows:
     scale >= 0 ? (int)(scale*0.5f + 0.25f): -(int)(-scale*0.5f + 0.25f).
     
  5. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,770
    Here is where I use +0.01, in my case. I could use probably 0.25, but decided to keep lower value.
     
  6. DavidSWu

    DavidSWu

    Joined:
    Jun 20, 2016
    Posts:
    183
    I see. I used .25 to land right in the center between "Bad" places.