Quaternion.ToAngleAxis is Unprecise?

In the code below, I create a quaternion with a rotation of 0.02 degrees and print the quaternion’s angle to the 5th decimal place.

Quaternion q = Quaternion.Euler(0.02f, 0f, 0f);
q.ToAngleAxis( out float qAngle, out Vector3 qAxis);

print(qAngle.ToString("F5"));

But according to Unity, the angle of the quaternion is:

0.00000

What’s causing this lack of precision? I’m so lost :frowning:

Because the angle is below the precision of 32 floating point numbers. The issue here is the way how rotations are represented as a quaternion. The quaternion that is created has the 4 values:

float halfRadians = angleInDegree * Mathf.Deg2Rad / 2;
x = 1 * Mathf.Sin(halfRadians);
y = 0 * Mathf.Sin(halfRadians);
z = 0 * Mathf.Sin(halfRadians);
w = Mathf.Cos(halfRadians);

As you may know a quaternion is always represented as a “vector” (x,y,z) and the cosine of half the angle around that vector. The issue here is that 0.02 degree yields a cosine value of 0.99999998476912.... However this is beyond the precision that a float can represent, so the value is rounded to 1. Though 1 represents an angle of 0°. The vector part itself is scaled down by sine of half the angle and results in 0.0001745329 which can be represented with a float. That means the quaternion may still work within some error margin, but the “normal” way of retrieving the the axis / angle just doesn’t work. Usually you would use “Acos” on the w value to retrieve half the angle which we would double. Though since that would yield a value of 0 that doesn’t really help.

You can still recover the angle to a higher precision by manually recovering the angle from the vector part instead of the scaler part. However you have to handle the sign manually. Something like this should work:

    Vector3 axis = new Vector3(q.x, q.y, q.z);
    float len = axis.magnitude;
    float angle = Mathf.Asin(len) * Mathf.Rad2Deg * 2f * Mathf.Sign(q.w);
    axis *= 1f / len;

Note that this solution will have similar issues when the half the angle gets closer to 90° (so angle is close to 180°).

So you may want to use this solution only if the absolute value of “w” is very close to “1”.