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 Am I losing float resolution when converting to float?

Discussion in 'Scripting' started by tsmspace, Jul 25, 2023.

  1. tsmspace

    tsmspace

    Joined:
    Oct 6, 2019
    Posts:
    39
    I have a save/load system that is comparing floats. Some of my saved objects are being lost. It doesn't seem to happen enough when played from the editor for me to notice,, but it seems to happen close to 100% of the time in the actual game build.

    please excuse my lack of knowledge, I'm learning as I go, have no background other than youtube tutorials, and have a few previous efforts to follow basic programming curriculums of various degrees of professionalism.

    When I first instance the objects, they are instanced into a parent object and then at the same time a list item is generated to store the objects position and an id interger. The player is able to delete the objects, however, so I need to remove those items from the list. To do this I compare the positions of each child object to the positions of the list, and then any that match are added to a new list, which is then passed to the save system.

    (all of this will probably need to change anyway, if I figure out how to allow the player to MOVE the objects,, I will certainly need to find a way to save each object ID based on the prefab at that time, but I still want to see if I can understand my present issue) .

    when the objects are reloaded, some of them are always missing. This does not seem to occur during loading, as every load after that is consistent. The objects that manage to get saved, are saved. Therefore I am assuming that my comparison is somehow not passing all of the objects that are in the parent.

    What I was thinking now,, is that perhaps when the position gets converted to the float variables, there must be some change in the value?? I can't see where anything happens other than float to float ,, but perhaps something changes to the position of the items in the parent object if the position is not perfect?? Therefore when instantiating, the value stored in the "savableObjects" list might have a final digit that is different from the actual object???


    Code (CSharp):
    1.  
    2. [System.Serializable]
    3. public class SavableObjects
    4. {
    5.     public int id;
    6.     public float px, py, pz;
    7.     public float rx, ry, rz, rw;
    8.  
    9.     public SavableObjects(int iden, Vector3 position, Quaternion rotation)
    10.     //public SavableObjects(id, Vector3 position, Quaternion rotation)
    11.     {
    12.         id = iden;
    13.         px = position.x; py = position.y; pz = position.z;
    14.  
    15.         rx = rotation.x; ry = rotation.y; rz = rotation.z; rw = rotation.w;
    16.     }
    17.     public int ReturnId()
    18.     {
    19.         int ret = id;
    20.         return ret;
    21.     }
    22.     public Vector3 ReturnPosition()
    23.     {
    24.         Vector3 pos = new Vector3(px,py, pz);
    25.         return pos;
    26.     }
    27.     public Quaternion ReturnRotation()
    28.     {
    29.         Quaternion rot = new Quaternion(rx,ry,rz,rw);
    30.         return rot;
    31.     }
    32. }
    33.  
    34. -------------------------------------------------
    35.  
    36. public void Save()
    37.     {
    38.         savename = saveInputField.text;
    39.         Debug.Log(savename);
    40.         savableObjects = randomPlacer.savableObjects;
    41.         Debug.Log(savableObjects.Count);
    42.         Debug.Log(ringspace.transform.childCount);
    43.         savableObjects2 = new List<SavableObjects> ();
    44.         foreach (Transform child in ringspace.transform)
    45.         {
    46.             for (int i = 0; i < savableObjects.Count; i++)
    47.             {
    48.                 if (child.transform.position == savableObjects[i].ReturnPosition())
    49.                 {
    50.                     savableObjects2.Add(savableObjects[i]);
    51.                     //Debug.Log(savableObjects[i]);
    52.                 }
    53.             }
    54.         }
    55.         Debug.Log(savableObjects2.Count);
    56.         savableObjects = savableObjects2;
    57.  
    58.         FileStream fs = File.Create(Application.persistentDataPath + "/" + savename + ".crs");
    59.         //Debug.Log(fs);
    60.         BinaryFormatter bf = new BinaryFormatter();
    61.         bf.Serialize(fs, savableObjects);
    62.         fs.Close();
    63.     }
     
  2. Hikiko66

    Hikiko66

    Joined:
    May 5, 2013
    Posts:
    1,302
    Yes you do. It's called floating point imprecision. For example, a double has a higher precision than a float, because it uses more memory, but it's also inaccurate. They are each accurate to a certain level.

    The best you can do is compare floating point numbers to see if they are approximately the same, within small margin of error. The margin of error is called epsilon for floats.
     
    Last edited: Jul 25, 2023
  3. tsmspace

    tsmspace

    Joined:
    Oct 6, 2019
    Posts:
    39
    This is making sense. I seem (upon some limited testing) to have greater save stability with this float truncation I took from a forum post somewhere.

    it's the Mathf stuff.

    Code (CSharp):
    1. foreach (Transform child in ringspace.transform)
    2.         {
    3.             Debug.Log(child.transform.position);
    4.             for (int i = 0; i < savableObjects.Count; i++)
    5.             {
    6.                 Debug.Log(savableObjects[i].ReturnPosition());
    7.                 if (Mathf.Round(child.transform.position.x * 100f) * 0.01f == Mathf.Round(savableObjects[i].ReturnPosition().x * 100f) * 0.01f && Mathf.Round(child.transform.position.z * 100f) * 0.01f == Mathf.Round(savableObjects[i].ReturnPosition().z * 100f) * 0.01f)
    8.                 {
    9.                     savableObjects2.Add(savableObjects[i]);
    10.                     //Debug.Log(savableObjects[i]);
    11.                 }
    12.             }
    13.         }
     
  4. Hikiko66

    Hikiko66

    Joined:
    May 5, 2013
    Posts:
    1,302
    Yeah. You should come up with a better comparison than positions. If you did want to compare floats where it is more appropriate, you can use Mathf.Approximately(float1,float2);
     
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Here's a very useful and very technical rundown on how floating point works. Getting a good idea of what works and what fails and why is your best option if you need to tame a staple numerical data type that's not going anywhere anytime soon.

     
  6. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,917
    Since you already have object ID's, use those for your "is this the same object" checks. Everyone else does that for pretty much the reasons you've discovered (float compares can be funny, and ID compares also work when objects are allowed to move).

    You could possibly fix your system to have 100% perfect float compares. You may even be making a simple easy-to-fix mistake. But every time you introduce new rules for positions, or a new system (adding an extra parent, making them physics objects ... ) you'll have to refix the system, and will miss a few that happen only 2% of the time, and it's just not worth the effort when comparing ID's is so easy and nearly error-proof.