Why are quaternions initialized to an invalid rotation by default?

This is a pretty simple question but I couldn’t find a straight answer for why this decision was made.

As you may or may not know quaternions in Unity are initialized to (0, 0, 0, 0), which is not a valid rotation. This is the default case if you create a new quaternion or if you let C# initialize with the default value. In other words, this is true in the following two cases:

Quaternion myRotation; // value: (0, 0, 0, 0)
// Or...
Quaternion myRotation = new Quaternion(); // value: (0, 0, 0, 0)

When you try to apply a rotation to a quaternion with this default invalid rotation it will fail. For example:

// This will fail to rotate myRotation
myRotation *= Quaternion.AngleAxis(90f, Vector3.up);

So instead you must remember to initialize a quaternion yourself like this:

Quaternion rotation = Quaternion.identity; // value: (0, 0, 0, 1)

(By the way, if you weren’t aware Quaternion.identity can be thought of kind of like the equivalent to Vector3.zero for quaternions. It lines up to the the x/y/z axes, so it effectively mean’s there is no rotation relative the those axes.)

Anyways, my question is: Why?

For comparison, this would be like if Vector3 default initialized to some bizarre invalid state where you couldn’t modify the vector until you set it to Vector3.zero (or some valid Vector3) first.

// In a much worse world...

Vector3 myPosition;

//...

// myPosition stays at (0, 0, 0) and you tear your hair out wondering where the bug is
myPosition += new Vector3(0f, 1f, 0f)

To be fair, most of the time when you create a new quaternion you immediately initialize it with some valid rotation. However, I see no reason why a quaternion should not be initialized to the identity quaternion. Am I missing something? Is there a good reason why it works this way? It seems like an annoying “gotcha” to remember for seemingly no benefit.


P.S. It’s sort of amusing that you can set a game object’s rotation to a new quaternion and it magically gets treated like being set to the identity quaternion.

transform.rotation = new Quaternion(); // value: (0, 0, 0, 1)
// This works just fine
transform.rotation *= Quaternion.AngleAxis(90f, Vector3.up);
3 Likes

This is a limitation of how value types (structs) work in .Net. Structs are allocated and zeroed inline and therefore cannot have constructors, i.e. always initialize their fields to 0. This makes them more efficient than classes, as the GC doesn’t need to track them and an array of structs can be created without needing to run any additional initialization code.

In the case of Quaternion, this means you’ll get (0, 0, 0, 0) by default. This does not only apply to creating a new instance but also to default-initialized fields or Quaternion in arrays. There isn’t really anything Unity could do about this, except using classes instead of structs but that would lead to performance and other usability issues.

7 Likes

Adrian is spot on. Keep in mind that it’s literally just the memory where the value is stored that is zero filled. So the default values of the individual types depends on their representation of all bytes being zero.

For example the IEEE 754 floating point format is designed such that all bytes being zero represents denormalized numbers and specifically a positive floating point value of “0f”. Otherwise the default value would be 1 (if the default is a normalized number and the exponent is stored directly as a signed number instead of an offset unsigned number).

Note that technically a quaternion with all values being 0 is not invalid. It just doesn’t represent a valid rotation as it’s not a unit quaternion and can not be normalized. It’s essentially the same with a vector. A vector can be interpreted as point or a direction. In that sense Vector3.zero is not a valid direction and it can not be normalized, and that’s the default value.

2 Likes

Interesting! I was totally unaware that structs had such a limitation. Thank you for enlightening me on this subject.