Quaternion.Angle is inaccurate

I have a strange issue with comparing angles. I do want to detect if an object rotated during the frame, using

if(Round(lastRotation, transform.rotation)) { // rotations are equal }

bool Round(Quaternion a, Quaternion b) {
	return Quaternion.Angle(a, b) < 0.001f;
}

However, that returns false for some quaternions that are absolutely equal. Here’s a debug print of those:

//  (first value: Quaternion.Angle(a,b); -- this should be zero! Then the Quaternions xyzw; then the eulerAngles xyz)

diff_rot: 0.03956468; (0.02575354000000000000, 0.96469100000000000000, -0.24099940000000000000, 0.10308820000000000000) != (0.02575354000000000000, 0.96469100000000000000, -0.24099940000000000000, 0.10308820000000000000); (28.05311000000000000000, 167.80090000000000000000, 0.00000024185890000000) != (28.05311000000000000000, 167.80090000000000000000, 0.00000024185890000000)

Why on earth do two exactly equal rotations (even equal in floating point!) return a non-zero angle? And not even a small one, but 0.03 degrees, which would be a visible change in rotation?

Here’s a script to test yourself this strange behaviour:
[52911-quaternionbug.zip|52911]

Corrected the mixup. Of course I mean that it returns false... Also, I added a script to test it yourself.

Seems like it might be an unfortunate outcome of floating point errors, you can use the following to avoid it, however its a bit messy: float Round(Quaternion a, Quaternion b) { float aL = Mathf.Sqrt(a.xa.x + a.ya.y + a.za.z + a.wa.w); float bL = Mathf.Sqrt(b.xb.x + b.yb.y + b.zb.z + b.wb.w); a.x /= aL; a.y /= aL; a.z /= aL; a.w /= aL; b.x /= bL; b.y /= bL; b.z /= bL; b.w /= bL; return Quaternion.Angle(a, b) < 0.001f; }

@felix. 'Should' is the key word there, try the following: Quaternion q = lastRotation*Quaternion.Inverse(transform.rotation); Debug.Log(string.Format("{0}, {1}, {2}, {3}", q.x, q.y, q.z, q.w)); On an object rotated with Eulerian rotation (90, 0, 0) and witness the madness! everything 0 except w value = 0.99999, producing an angle difference of 0.03.... Have you tried the code I posted, it returns 0 degrees, not the 0.03... value

Confirming this is still present in 5.3.4f. I found this post by searching for 0.03956468! Can someone provide a link to the bug report so I can determine when it gets fixed? Thanks,

Im making a tetris game, and was using rotations, and I noticed that too, in my case was just question of 0.00001º, so I had to find another alternative as I did and I bet there are to you too.

3 Answers

3

Update: on 5.5.1f
The FP error has grown from 0.03956468 to 0.06852804. This is what Angle can produce when comparing two identical rotations, in my case 142.0884.

Im making a tetris game, and was using rotations, and I noticed that too, in my case was just question of 0.00001º, so I had to find another alternative as I did and I bet there are to you too.

It seems that Quaternion.Angle function is not accurate. Try the following function instead:

float AcurateAngle(Quaternion a, Quaternion b)
{
     GameObject go = new GameObject();
     go.transform.rotation = a;
     Quaternion.Inverse(b).ToAngleAxis(out float angle, out Vector3 axis);
     go.transform.Rotate(axis, angle);
     go.transform.rotation.ToAngleAxis(out angle, out _);
     Destroy(go);
     return angle;
}

Wow, creating and destroying a gameObject just to calculate an angle seems brutally inefficient