Why does normalizing a vector not work? Bug? [GetSafeNormal(), GetUnsafeNormal()]

Hi all,
I’m trying to normalize a vector received by UTankNavMovementComponent::RequestDirectMove(), but logging the vector to the console shows that it hasn’t been normalized at all. I tried it with and without providing a float parameter in GetSafeNormal() and I also tried GetUnsafeNormal().
I’m either receiving a (0, 0, 0) vector or something huge from the normalization attempt.
A workaround that I found (but I don’t think it leads to the exact same desired behavior) is to use MoveVelocity.GetClampedToSize(-1.0f, 1.0f) instead of normalization.

It’s really confusing me because it seems such a simple thing… Any help much appreciated! Using UE 4.23.0.

Screenshot of the console output (two AI actors):

My CPP file (TankNavMovementComponent.cpp):

#include "TankNavMovementComponent.h"
#include "DrawDebugHelpers.h"
#include "TankTrack.h"

#pragma region Overrides

void UTankNavMovementComponent::RequestDirectMove(const FVector& MoveVelocity, bool bForceMaxSpeed)
{
	// No need to call Super. Logic will be replaced.

	const FVector TankForwardVectorNormal = GetOwner()->GetActorForwardVector();
	UE_LOG(LogTemp, Warning, TEXT("[%s] TankForwardVectorNormal: %s, IsNormalized: %s"),
		*GetOwner()->GetName(),
		*TankForwardVectorNormal.ToString(),
		TankForwardVectorNormal.IsNormalized() ? TEXT("True") : TEXT("False"));

	UE_LOG(LogTemp, Warning, TEXT("[%s] MoveVelocity: %s, IsNormalized: %s"),
		*GetOwner()->GetName(),
		*MoveVelocity.ToString(),
		MoveVelocity.IsNormalized() ? TEXT("True") : TEXT("False"));

	// TODO: Why doesn't this work when normalizing with GetSafeNormal()?
	const auto MoveVelocityNormal = MoveVelocity.GetSafeNormal();
	//const auto MoveVelocityNormal = MoveVelocity.GetClampedToSize(-1.0f, 1.0f);
	UE_LOG(LogTemp, Warning, TEXT("[%s] MoveVelocityNormal: %s, IsNormalized: %s"),
		*GetOwner()->GetName(),
		*MoveVelocityNormal.ToString(),
		MoveVelocityNormal.IsNormalized() ? TEXT("True") : TEXT("False"));

	// Calculates the ForwardThrow.
	const float ForwardThrow = FVector::DotProduct(TankForwardVectorNormal, MoveVelocityNormal);
	UE_LOG(LogTemp, Warning, TEXT("[%s] ForwardThrow: %f"), *GetOwner()->GetName(), ForwardThrow);

	DrawDebugLine(GetWorld(), GetOwner()->GetActorLocation(), GetOwner()->GetActorLocation() + MoveVelocityNormal, FColor::Magenta, false, -1, 0, 1);

	// Applies the ForwardThrow.
	IntendMoveForward(ForwardThrow);

	// Calculates the IntendTurnRight. Order of vectors matters for CrossProduct!
	const float RightThrow = FVector::CrossProduct(TankForwardVectorNormal, MoveVelocityNormal).GetClampedToSize(-1.0f, 1.0f).Z;

	// Applies the IntendTurnRight.
	IntendTurnRight(RightThrow);
}

#pragma endregion

// ... rest of file ...

Myself and one of my software engineer classmates have the same issue.

A solution for the moment is to use .Normalize() instead.

Actually this isn’t working for me either. Supposedly my friend had some success with this work around though.

The following requires verification and is only inferred from reading the source code:
I would assume your problem is directly related to how Unreal uses SSE tricks to calculate an approximated inverse square root inside GetSafeNormal(). It may not be as resilient to very high numbers as sqrtf.
Conversely, inside GetClampedToSize(), which in turn calls Size(), sqrtf is used directly, which explains why the operation doesn’t fail.
You could easily write your own normalization routine. It won’t be as performant probably, but it will be more resiliant to high numbers if this is in fact the root of the issue:

float StableNormalize(const FVector& v) const
{
    const float size = v.Size();
    return size == 0 ? FVector::Zero : v / size;
}

Can you give an example of a vector that was failing for you?

The last sentence in particular is extremely odd.

This basically solved the problem, although I have no idea why the problem started in the first place… My location values aren’t out of the ordinary. Here’s the code i’m using that works.

bool ACrossStampede::StableNormalize2D(FVector& _v)
{
	const float size = _v.Size();
	if (size == 0.0f)
	{
		_v = FVector::ZeroVector;
	}
	else
	{
		_v = _v / size;		
		_v.Z = 0.0f;
	}

	return size == 0.0f;
}

Sure.

// Calculating direction
FVector PlayerLocation = m_PlayerCharacter->GetActorLocation();
FVector OwnerLocation = MyOwner->GetActorLocation();
FVector vDirection = PlayerLocation - OwnerLocation;
//FVector vDirection = FVector(2843.969f, 2269.591f, -1427.615f);
FVector vDirectionNorm = vDirection.GetSafeNormal2D();

This is what it looked like before i implemented the fix of using my own normalizing function. I was logging each of these variables to check their validity, PlayerLocation and OwnerLocation were both valid, also vDirection had the correct output. When I hardcoded the values for vDirection, the result of vDirection.GetSafeNormal2D() was correct and not bugged.

This seems to be related to a compiler bug introduced in or around VS 2019 16.3.3 that MSFT is working to fix:
https://developercommunity.visualstudio.com/content/problem/734585/msvc-142328019-compilation-bug.html

They acknowledged the problem on 10/08/2019

There is a workaround solution(to use an older compiler version) in that link if you need to update your build immediately.

UPDATE:
Confirmed this was fixed in VS 2019 16.3.9