Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question about floating point imprecision and the measurement of a raycast's distance

Discussion in 'Scripting' started by egonspengler_84, May 31, 2021.

  1. egonspengler_84

    egonspengler_84

    Joined:
    Jan 26, 2021
    Posts:
    153
    So I have a square falling and hitting the ground with a raycast projecting from the square's bottom that hits the ground beneath it. If I have an if statement that outputs a message when the raycast's distance is equal zero it works perfectly. So for example there's no issues when I write the following:

    Code (CSharp):
    1. if(hit && hit.distance == 0){
    2.  
    3.            Debug.Log("Raycast distance equals zero");
    4.  
    5. }
    But if I write the following if statement then no message in the console appears at all:

    Code (CSharp):
    1. if(hit && hit.distance == 0.015f){
    2.  
    3.            Debug.Log("Raycast distance equals 0.015f");
    4.  
    5. }
    I don't understand why the first if statement works but the 2nd one does not? I would of assumed that both of the if statements would not work because of floating point imprecision?
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Despite their reputation, floats don't just randomly mutate their values, it's a lot more nuanced. 0 will be 0. 2 * 2 will be 4. It's when you start multiplying values by 0.0114372 delta time that your precision starts to fall apart.

    Still, never do a direct equality comparison, use Mathf.Aproximately.
     
    Last edited: May 31, 2021
  3. egonspengler_84

    egonspengler_84

    Joined:
    Jan 26, 2021
    Posts:
    153
    Well that's the thing, I've tried using Mathf.Approximately but I can't seem to get it to work.

    Here's the code that I've written:


    Code (CSharp):
    1.     void FixedUpdate()
    2.     {
    3.         Bounds bounds = _col2D.bounds;
    4.         botRight = new Vector2(bounds.max.x,bounds.min.y);
    5.         velocity = Vector2.down * gravity;
    6.         _rb.MovePosition(_rb.position + velocity);
    7.  
    8.  
    9.         RaycastHit2D hit = Physics2D.Raycast(botRight,Vector2.down ,rayLength,_collisionMask);
    10.         Debug.DrawRay(botRight,Vector2.down * rayLength, Color.green);
    11.  
    12.      
    13.         if(hit && Mathf.Approximately(hit.distance, 0.015f)){
    14.  
    15.                
    16.                 Debug.Log("Mathf.Approx works");
    17.              
    18.            
    19.         }
    20.  
    21.          
    22.     }
    So basically what I'm saying is that if the raycast has hit the ground(which has a LayerMask) and the distance of the raycast is approximately 0.015f then the message "Mathf.Approx works" should appear in the console. However it doesn't and I'm confused as to why this is?
     
  4. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    Log the distance and see what value you're getting back. 0.015f is very specific, you'll only get a true result for a value between something like 0.01499999999 and 0.01500000001.
     
  5. egonspengler_84

    egonspengler_84

    Joined:
    Jan 26, 2021
    Posts:
    153
    I've done what you've asked and I've uploaded a video of me logging the distance in the console as you can see here in this Youtube clip:


    As you can see the closest that hit.distance will equal to is " 0.1428347". The message "Mathf.Approx works" which is in the if(hit) statement does not output to the console.
     
  6. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
    I don't think anyone would consider those last two values, 0.14 or 0.012, approximately equal to 0.015. Are you sure you don't want a less than check here?
     
  7. egonspengler_84

    egonspengler_84

    Joined:
    Jan 26, 2021
    Posts:
    153
    See I have the bounds of the square inset by a skin width that's equal to 0.015f. So if I want to have the square stop flush with the ground(in other words have it collide with the ground) I would have to make sure that the maximum distance that the square can travel downwards on the Y-axis is determined when the raycast's distance is equal to 0.015f rather than zero.

    If the bounds of the square were not inset I could write the following code to make sure that the square would collide flush with the ground:

    Code (CSharp):
    1.  
    2. private float gravity = 0.5f;
    3. Vector2 velocity = Vector2.down * gravity;
    4.  
    5.  
    6. if(hit){
    7.  
    8.   velocity = Vector2.ClampMagnitude(velocity,hit.distance);
    9.  
    10.  
    11. }
    And this works perfectly when no skin width is applied. But when I try to inset the Bounds by 0.015 floats obviously I start getting issues with this code as you already know. That's why I was trying to figure out how to use Mathf.Approximately correctly you see.

    I don't understand what you mean by a "less than check"? Like some sort of if statement that states if hit.distance is less than skin width? Something along those lines?
     
    Last edited: May 31, 2021