How does Unity normalizes a Vector?

I was playing around, having fun, creating my own vector class, and the need to normalize it arose, I wrote a property and a function, the same as Unity, then I printed the result in the console and compared it with a vector with the same values but normalized by Unity’s functions, the numbers were off, my result threw something similar to 0.707, while Unity’s threw a rounded 0.71, for the value X of both vectors.

At first, I thought Unity was using fast inverse square root, so I tried that out, and the result was off again and by much more than the first time.

Here’s the code for the inverted square root:

float InvSqrt(float x)
{
    float xhalf = 0.5f * x;
    int i = BitConverter.SingleToInt32Bits(x);
    i = 0x5f3759df - (i >> 1);
    x = BitConverter.Int32BitsToSingle(i);
    x = x * (1.5f - xhalf * x * x);
    return x;
}

What really caught my eye is that there’s a warning in Unity’s API on the Vector.magnitude page that says that you should use sqrMagnitude instead since magnitude uses a square root operation, yet there’s no warning on the normalize or Normalized pages.

Does anyone have any idea how Unity normalizes vectors, or if there is a way to normalize a vector without using a square root? I wasn’t able to find anything on the net, and I’ve been searching for 3 hours or so.

@garypettie

Hey @Yee!
Nice work implementing the fast inverse square root function (even though it’s missing the delightful original comments! :joy:).

When it comes to understanding how Unity do things behind the scense, you can usually find this in various places online (Stack Exchange is usually a good starting point).
Some of the source code is actually on github though. Here’s a link to the Vector3 class:
https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Vector3.cs

Of note to your question, here is the code for their implementation of Normalize and Magnitude…

Normalise:

[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public static Vector3 Normalize(Vector3 value)
{
    float mag = Magnitude(value);
    if (mag > kEpsilon)
        return value / mag;
    else
        return zero;
}

Magnitude:

[MethodImpl(MethodImplOptionsEx.AggressiveInlining)]
public static float Magnitude(Vector3 vector) 
{
    return (float)Math.Sqrt(vector.x * vector.x + vector.y * vector.y + vector.z * vector.z); 
}

You’ll find that a lot of the math based functions in Unity are fairly simple wrappers of the fuctions found in the Math library. The big difference between the Math library and the Mathf library is that Math library generally works with doubles and Unity.Mathf casts the results to a float. This can introduce some floating-point errors but it’s usually not that noticeable.

Thanks! I spent way too much time trying to find this.

So they do use square root, interesting, I’ll keep that in mind.

It’s also worth noting that the fast inverse square root algorithm isn’t actually all that fast on modern processors (a lot has changed since '96). It’s also not 100% on accuracy, so you may see some errors creeping in unexpectedly (it’s got an error margin of around 0.175%).

Honestly, the sqrt operation isn’t all that slow these days and unless you’re trying to edge out every ounce of performance there are far bigger things in your code that you could probably optimize first. Calling magnitude every frame is pretty wasteful if sqrMagnitide would also do the job but, if you’re calling it fairly infrequently, then there’s really no need to worry about it.

If you really need the inverse square root for something then you could always use the rsqrt() method from the Mathematics library, which is optimised for modern hardware.

1 Like

This is what bothered me about normalizing vectors, the calculation uses the magnitude of the vector and there’s no way to use sqrMagnitude instead as far as I’m aware, but no one ever talks about it, I suppose it’s because of what you said, it’s not really that much of a worry, even if I’m calling it during each update tick.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms