Rotation snapping overshooting on certain rotations.

Hi, I’ve been trying to get a function working to snap rotation of a block on each axis to increments of 90 degrees. The problem I’ve been having is that, no matter how I seem to approach applying rotation, I get slight miscalculations at certain rotations.

The code:

    void RotateBlock()
    {
        if (buildMode == BuildMode.Active)
            if (!IsACube())
            {
                float xAngle = 0;
                float yAngle = 0;
                float zAngle = 0;

                if (Input.GetKeyDown(KeyCode.G))
                    xAngle = 90f;

                if (Input.GetKeyDown(KeyCode.R))
                    yAngle = 90f;

                if (Input.GetKeyDown(KeyCode.F))
                    zAngle = 90f;

                if (mirrorMode == MirrorMode.Inactive)
                {
                    tempBlock1.transform.Rotate(
                        Mathf.RoundToInt(xAngle),
                        Mathf.RoundToInt(yAngle),
                        Mathf.RoundToInt(zAngle),
                        Space.World);
                }

                else
                {
                    tempBlock1.transform.Rotate(
                        Mathf.RoundToInt(xAngle),
                        Mathf.RoundToInt(yAngle),
                        Mathf.RoundToInt(zAngle), 
                        Space.World);

                    tempBlock2.transform.Rotate(
                        Mathf.RoundToInt(xAngle),
                        Mathf.RoundToInt(-yAngle),
                        Mathf.RoundToInt(-zAngle), 
                        Space.World);
                }
            }
        else return;
    }

On -90 and +90 rotations for each axis, I’m getting a deviation of -/+ 0.00001 which is causing me problems elsewhere because I need to be able to check an object’s rotation against a vector. I’m also getting a weird assignment (-1.525879e-05) to the Y and Z axes every other time the X rotation snaps to -180.

I’m sure I’m just doing something stupid, but I’m at a loss as to what. Any help would be hugely appreciated. For the record, I’ve tried each of the angles being an int, rounding the rotational assignment with Mathf.Round, Mathf.RoundToInt, or without any rounding at all.

Ah yes floating point approximation strikes yet again… TLDR: positions and rotations in Unity cannot be reliably set and read due to the fact that floating point numbers cannot exactly express many fractions. In reality you are just going to have to deal with it, but usually there is a pretty simply workaround depending on your application. If you give me more information on why you need to check the rotation angles or why you need to check them against a vector I can help out with that, but generally doing things like if (rotation.eulerAngles.y == 90) is bad practice.
**
The reason these errors happen is because floating point numbers use base 2, and not all simple base 10 numbers can be expressed simply in base 2. Just like how 1/3 cannot be expressed in a finite number of decimal places in base 10, similar things happen in base 2 when trying to express some simple fractions. The solution is just to go out to as many decimal places as will fit into a float and dropping anything after that. This results in values that are extremely close to the desired value but not exact. This is often only really an issue with fractions, as generally you don’t get large enough integers to break down floating point precision. But even when rotating by small integers, like 90 degrees, you will still get issues as Unity converts these angles into a Quaternion, and the internal values in a quaternion are usually small fractions resulting in some floating point error. There are two common solutions to this kind of issue:

  1. You can keep track of the “desired” rotation yourself, instead of reading it from the objects rotation directly. That way you know your “desired” rotation is exactly 90 degrees, while you actual rotation is very very very close to 90 degrees.
  2. You can allow for a small degree of error. Instead of checking if the rotation is exactly 90 degrees, you can check if the rotation is 90 degrees ± 0.001 degrees

Another odd thing I noticed, when these blocks are reinstantiated from the save file and the rotation is assigned from an explicitly declared Quaternion.Euler, the same behaviours seem to be happening.