Quaternion.FromToRotation() produces incorrect results (possible precission error)

Short story:

rotation = Quaternion.FromToRotation(Vector3.up, newup);

returns a 0-rotation (instead of the correct rotation), when the 2 vectors are almost parallel.
Basically, whenever the resulting rotation would be smaller than 0.01 degrees, the result is 0 degrees instead.
While this may seem small, it’s noticeable (and bigger than what I would expect the precision error to be), and a real hindrance for me.

Does anyone know if this is a bug in this function, and if there’s a way around this?

I replaced it with:

rotation = Quaternion.AngleAxis(Vector3.Angle(Vector3.up, newup), Vector3.Cross(Vector3.up, newup));

And the error is much smaller
(now it’s only a 0-rotation when the actual angle is smaller than ± 0.001 degrees, so 10x as small)
Which I can live with I guess.

But this does surprise me, as I would expect the error to be bigger in this case, not smaller.

If anyone can enlighten me it would be much appreciated.

There is definitely going to be float error no matter what.

+/- 0.001 is about where I’d expect it. Maybe a little smaller, but I have no expectation of accuracy beyond 4 sig fractional values (I consider the 1’s spot sig value even if 0, as unity often prefers degrees).

Quaternion.FromToRotation is unfortunately an internal operation, so I’m not 100% sure how they implement it. But I’m willing to bet somewhere in their they perform a scaling (multiplication, power, root, something like that) that shreds the sig value range down to the 3 sig values you see. Resulting in all operations after that to suffer from that same error.

An option you have is to implement your own calculation yourself for FromToRotation:

using UnityEngine;
using System.Collections;

public class TestScript02 : MonoBehaviour {


    // Use this for initialization
    void Start () {

        var v1 = Vector3.up;
        var v2 = VectorUtil.RotateAroundAxis(v1, 0.0005f, Vector3.right);
        var q1 = Quaternion.FromToRotation(v1, v2);
        var q2 = VectorUtil.FromToRotation(v1, v2);

        Debug.Log(Vector3.Angle(v1, v2).ToString());
        Debug.Log(q1.eulerAngles.ToDetailedString());
        Debug.Log(q2.eulerAngles.ToDetailedString());

    }

}

public static class VectorUtil
{

    public static Quaternion FromToRotation(Vector3 v1, Vector3 v2)
    {
        var a = Vector3.Cross(v1, v2);
        var w = Mathf.Sqrt(v1.sqrMagnitude * v2.sqrMagnitude) + Vector3.Dot(v1, v2);
        return new Quaternion(a.x, a.y, a.z, w);
    }

    public static Vector3 RotateAroundAxis(Vector3 v, float a, Vector3 axis, bool bUseRadians = false)
    {
        if (bUseRadians) a *= Mathf.Rad2Deg;
        var q = Quaternion.AngleAxis(a, axis);
        return q * v;
    }

    public static string ToDetailedString(this Vector3 v)
    {
        return System.String.Format("<{0}, {1}, {2}>", v.x, v.y, v.z);
    }

}

Just got the q2 using my own implementation to return:

Vector3.Angle didn’t even get that level of precision.

1 Like

Honestly, that version of it I just wrote is getting full real 7 digits of precision (no matter if it’s off of the 1’s spot… which is my usualy expectation for unity).

Just put in:

        var v2 = VectorUtil.RotateAroundAxis(v1, 0.0000012345678f, Vector3.right);

and got:

Which is 0.000001234568f

That 7 rounding up to 8 is what I would expect in a perfect 32-bit float precision scenario.

I never noticed this level of error in Quaternion.FromToRotation before.

So I’ma add it to my QuaternionUtil: