How can I check if a component of a CFrame is NaN?

I’m trying to find a way to figure out whether or not there is a NaN (not a number) component in a CFrame. I’ve created a few solutions, however, I don’t think they’re the best they can be. The first problem was that when you try to get the type of a variable that is NaN, it will tell you it’s a number:

print(math.asin(2)) --> nan
print(type(math.asin(2))) --> number

Since there’s no unique type I attempted a few other solutions

local function HasNaNComponent(cframe)
	return tostring(cframe:GetComponents()):find("nan") ~= nil
end

print(HasNaNComponent(CFrame.new(math.asin(2), 0, 0))) --> true
print(HasNaNComponent(CFrame.new(1, 0, 0))) --> false

This was giving me the correct result, however, turning the components into one big string felt like an unnecessary step, so I tried looping through the components. Apparently, two NaN values are not equal to each other, even when you use the same method to get both values, for example, math.asin(2) ~= math.asin(2).

local function HasNaNComponent(cframe)
	for _, component in pairs{cframe:GetComponents()} do
		if component ~= component then
			return true
		end
	end
	return false
end

This function yielded the same results as the previous one. However, there’s a problem with this one as well, I need to turn the components into a table. Is it at all possible to avoid creating tables or strings to get the result I’m looking for?

What’s the matter with turning them into strings or tables? Are there performance implications from using them?

Try turning them into strings and then compare

The property of them not being equal to themselves is what makes the second function possible. Any normal number, like 1, is equal to itself.

print(1 == 1) --> true
print(math.asin(2) == math.asin(2)) --> false

Since NaN values aren’t equal to themselves, and every other number value is, I can find the NaN value without searching for it in a string.

The function will be called very frequently, so I need it to be as fast as possible, while not being a drag on the game’s performance.

Can I ask what situation you need this for? Because it seems like a strange thing to want to do.

Anyways, if you’re really concerned about making a table (which you shouldn’t be) you could just write out the twelve if-statements by hand. Or maybe check value-equality on the Position and Right/Up/LookVectors, that might work because Vector3 equality works differently than CFrame.

I’m lerping a model from one position to the next, and for whatever reason, sometimes the lerp is giving me NaN values. So there might just be a problem with the way I’m lerping. This is the function:

local function LerpModel(model, cframe)
	local start = model:GetPivot();
	local t = 0;
	
	while t < 1 do
		model:PivotTo(start:Lerp(cframe, t));
		t += Heartbeat:Wait()*rate;
	end;
end;

I’m not so sure about that because of floating point precision. I guess it would work fine if you’re comparing a reference to itself (like the triple = operator)

That sounds impractical. CFrames are riddled with metatables, hooks, and self-calling functions and they are by nature quite heavy (when compared to Vector3s and other datatypes). I don’t think micro-optimizing that function will make any significant difference.
If you still want to try, you’ll need to benchmark the function with different configurations, such as using ipairs over pairs or generalized iteration, tweaking the logic expression, switching out the colon operator, etc.

Hmm. I’m not sure. Are you certain your target CFrame doesn’t have any NaNs in it when calling this function?

I doubt that PivotTo or Lerp would be introducing them, but maybe.

I have another function that rotates the model in the direction of the target V3, then makes the model start moving in that direction. The first lerp I do to rotate it toward the target isn’t giving me any problems, however, moving it towards the target is.

local angle = CFrame.lookAt(root.Position, position);
local _, _, _, m00, m01, m02, _, _, m12, _, _, m22 = angle:GetComponents();
	local oriented = CFrame.new(position)*CFrame.Angles(
		math.atan2(-m12, m22), 
		math.asin(m02), 
		math.atan2(-m01, m00)
	);

The variable named “oriented” is the cframe being inputted to the function. I will admit, that I used a strange method of keeping the direction of the rotation when the model is moving.

I would definitely try clamping the value of the matrices. I’ve encountered a similar problem before where dot product returned a number that’s slightly beyond the [-1, 1] interval, which caused my math to break down.