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 Floats cannot be compared in any way. Possible alternatives using ints?

Discussion in 'Scripting' started by GarbageHaus, Jun 5, 2023.

  1. GarbageHaus

    GarbageHaus

    Joined:
    May 1, 2023
    Posts:
    5
    Hello all! Apologies if this is a dumb question, but I'm having trouble getting any float values to be comparable.

    Consider the following statement:

    Code (CSharp):
    1. if (forwardSpeed <= maxSpeed) {
    2. //do stuff
    3. }
    I have other code, but this is really the only relevant portion. forwardSpeed and maxSpeed are floats. No matter how I define them, this statement evaluates to false. I have tried defining these a variety of ways including:

    Code (CSharp):
    1.  
    2. public float forwardSpeed = 1f;
    3. public float forwardSpeed = 0.0001f;
    4. public float forwardSpeed = 1;
    5.  
    With maxSpeed being set to either 50, 50f.

    To rule out any feasible problem with the rest of the code, I used (forwardSpeed != maxSpeed) which does work as expected.

    With the passion of a billion burning suns do I hate the nonsense pseudo-math concept that is a float, but I am required to use them because some functions in Unity require floats.

    I want to know:
    Since floats cannot be compared using less/greater than operators, would it make sense to make forwardSpeed and maxSpeed be two ints, then have 2 other floats like "forwardSpeed_float" and "maxSpeed_float", then do some kind of conversion? I'm not even sure how that would look.

    I assume Unity/CSharp requires me to define the 4 variables, then convert them to floats during the Update() block, then use the ints for the if statement but then use the floats to actually use the unity function. Should I be concerned about a performance hit doing it this way? Is there a better way to do on-the-fly conversions? Or is there some other method to trick Unity into comparing floats?

    Thank you,
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,378
    <= should work on floats

    The one that is difficult is a == comparison since float error can cause values that look like they're the same to be slightly not equal by very small amounts. In that case you should use Mathf.Approximately:
    https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html

    I am noticing that your floats are 'public', and if this is a MonoBehaviour/ScriptableObject that means they're serialized. Are you sure that the values set in the inspector match what you've written here? Cause if you're just changing them in code, they're possibly getting overwritten by the serialization on start.

    Try Debug.Log'n the forwardSpeed just before the if statement:
    Code (csharp):
    1. Debug.Log($"{forwardSpeed} <= {maxSpeed} = {forwardSpeed <= maxSpeed}");
    2. if (forwardSpeed <= maxSpeed) {
    3.     //do stuff
    4. }
     
  3. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,140
    https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html
    Will allow you to do an approximate check, however, that doesn't appear to be what you want. Since you have a <= check, then something else is going wrong since it should be true if the condition is true. So, that being said, you have something else going on. Have you tried printing out the value of forwardSpeed with Debug.Log calls? Since the value is public, have you checked it in the inspector to make sure it's the value you expect?

    Unfortunately, you are incorrect. They can be compared with < and >. So you have something else wrong in your code. Time to debug!

    @lordofduct Was a bit faster! :D
     
    orionsyndrome and lordofduct like this.
  4. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    You don't seem to understand floating point values very well.
    First of all, you're doing something else wrong.
    forwardSpeed <= maxSpeed
    never fails unless you don't know what you're doing.

    No, there is no hidden curse associated with floating points, they work just fine. There is no need to get superstitious over something you read somewhere, because you haven't fully understand what it was all about in the first place.

    Floating point values have an issue of being imprecise for two reasons:
    1) they are based on a binary system, not on a decimal one, so some values do not align perfectly,
    2) they have limited storage/precision, so comparing two values across many orders of magnitude, or equating them with a fixed value can frequently fail.

    None of these things have anything to do with you case.
    Comparing floats with <, <=, >, >= is the safest way to work with them.
     
  5. icauroboros

    icauroboros

    Joined:
    Apr 30, 2021
    Posts:
    99
    You should use tolerance when check equality of two float. Which all games do this way(unless needs to be super deterministic for multiplayer RTS etc.)

    https://www.jetbrains.com/help/rider/CompareOfFloatsByEqualityOperator.html

    Code (CSharp):
    1. double add = 0;
    2. for (int i = 1; i <= 11; i++)
    3. {
    4.   add += 0.1;
    5. }
    6. double multiply = 0.1 * 11;
    7. Console.WriteLine(add == multiply); // false
    Code (CSharp):
    1. double add = 0;
    2. for (int i = 1; i <= 11; i++)
    3. {
    4.   add += 0.1;
    5. }
    6. double multiply = 0.1 * 11;
    7. var tolerance = 0.000000001;
    8. Console.WriteLine(Math.Abs(add - multiply) < tolerance); // true
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    That's a red-herring. Floats can be compared with greater than / less then. However your issue is that your variable is declared as public and is therefore serialized by Unity in the inspector. So the value you may assign in a field initializer is completely irrelevant with the exception when you first attached your script to the gameobject. Check the inspector for the actual value. That's the point of having serialized values, to set the value in the inspector. If you don't want the variable to be serialized, don't make the variable public or if you really want it to be public (which often times is not a good sign) you can use the NonSerialized attribute so the variable will not be serialized and will not show up in the inspector.
     
  7. GarbageHaus

    GarbageHaus

    Joined:
    May 1, 2023
    Posts:
    5
    Thanks for this, (and thank you @Bunny83 as well for the insight) I did add this code and noticed it was displaying: 2<=1=false, which seems to be nonsense since there's only one instance of this object, and only one instance of this script. Changing these to Private resolved the problem. Looking through some other documentation it seems that these values are saved somewhere based on what Unity thinks the values should be. After it was resolved, I changed it back to Public, but this didn't cause it to reproduce... which leaves me more confused...

    Why does the inspector, change some, but not all, of the public variables and only change them sometimes?
     
  8. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    The inspector saves all public values (of serializable types) always. There's no inconsistency. Check your inspector.
     
  9. GarbageHaus

    GarbageHaus

    Joined:
    May 1, 2023
    Posts:
    5
    Sorry, I see what you mean now. When I tested this was when the game was running. Now I see that the inspector retains the values before I run the game. I was not expecting that difference.
     
  10. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Why do you think it's a nonsense pseudo-math concept? Can you think of a more nimble and versatile implementation, engineering-wise? I mean the whole world would like to move onto that. FP is not someone's crazy idea, it's the best compromise we've got, just slightly standardized for better hardware support.

    I've tried designing my own signed number type once. The spec was easy: you got 2 bytes, try and use up all the value space, make some accommodations for the decimal part. The design was simple: the little room I had for the decimal part would get sacrificed the bigger the integer part got, or else the integer part would have to remain small. I actually ended up with a half-assed floating point. You might want to try that, see if you'd hate yourself with such passion.

    Btw, nearly everything in Unity is floats. And everything on GPU is floats. It's better to get used to them.
     
    PraetorBlue and Bunny83 like this.
  11. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,834
    Er,
    2 <= 1
    is false, though. Did you mistake the direction of the angle brackets also?
     
  12. GarbageHaus

    GarbageHaus

    Joined:
    May 1, 2023
    Posts:
    5
    Well no. 2<=1=false does make sense. It's just that in the context of what was actually defined, 2 was nowhere to be found. Seems it was what was described above, the inspector values were present because it was a Public value and I didn't realize this wouldn't be overwritten by what was present in the actual script.
     
  13. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    Get in the habit of questioning and verifying your assumptions with log statements or a debugger and these mysteries will reveal themselves much more quickly.