I am trying to write down a unit test to check if a new vector3 returns (cos(angle), 0, sin(angle)). So if pass angle = 0, it should returns (1,0,0).
I know it is something related to the Episilon, but I don't know how to formulate it.
Here is my code:
public Vector3 SetForceDirection(float angleInDegrees)
{
float angleInRadians = angleInDegrees * Mathf.Deg2Rad;
return new Vector3(Mathf.Cos(angleInRadians), 0, Mathf.Sin(angleInRadians));
}
And my unit test:
void SetforceDirection_Angle0_Return_1_0_0()
{
CtrlHorizontal _ctrlHorizontal = new CtrlHorizontal();
Vector3 result = _ctrlHorizontal.SetForceDirection(0);
Assert.That(result == new Vector3(1, 0, 0));
}
Thanks!
Use Debug.Log
to check what values Mathf.Sin()/Cos()
return actually. The default precision used for Vector3.ToString()
is 5 digits after the dot, iirc. To get higher precision in the debug logs, use
Vector3 v = new Vector3(1.1234567891011, 0.00000001, 2.2222222222);
Debug.Log(v.ToString("F10"));
This should print the vector values with 10 digits after the dot.
So, what's actually happening?
Let's take for example the angle 90 degrees. The result of Mathf.Cos()
will be:
tmp1 = 90f * Mathf.Deg2Rad; // 1.570769
tmp2 = Mathf.Cos(tmp1); // -4.371139E-08
So, Mathf.Cos()
returns -4.37139E-08
rather than 0
. The reason is floating points precision limit. It breaks a lot of things in computer science.
The results you get from Mathf.Sin()/Cos()
with angles like 0, 90, 120 won't give you 0's and 1's, instead they will give you numbers like 4.371139E-08. This value is very very very close to zero but it's not a zero. So, when you compare such results to the new Vector
1, 0 and 0 in your code the equation fails.
Although, the Vector3 == Vector3
operation in Unity already should apply some rounding so the values that are really close to each other will be treated as equal. As opposed to Vector3.Euals()
which does the exact comparison.
Here is the source code of the Vector3 == Vector3
operation from Unity's source code repo: https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/Math/Vector3.cs
// Returns true if the vectors are equal.
public static bool operator==(Vector3 lhs, Vector3 rhs)
{
// Returns false in the presence of NaN values.
float diff_x = lhs.x - rhs.x;
float diff_y = lhs.y - rhs.y;
float diff_z = lhs.z - rhs.z;
float sqrmag = diff_x * diff_x + diff_y * diff_y + diff_z * diff_z;
return sqrmag < kEpsilon * kEpsilon;
}
As you can see, it checks if the squared difference between the vectors values is less than kEpsilon * kEpsilon
( kEpsilon = 0.00001
, so the squared value is 0.000000001
).
The tricky thing here is Mathf.Cos()
of 90 degrees might return 0.00001
, and the ==
operation will compare 0.00001 * 0.00001 < kEpsilon * kEpsilon
and it will return false, because these values are the same actually, so the result of the operation
Vector3 v1 = new Vector3(1, 0, 0); // your "checkout" vector
Vector3 v2 = new Vector3(1.00001, 0, 0); // the result of Mathf.Cos()/Sin() in your code
// in the Vector3 == Vector3 operation Unity does:
float sqrmag = 0.00001 * 0.00001 + 0 * 0 + 0 * 0;
return sqrmag < kEpsilon * kEpsilon;
0.000001 * 0.000001
is not less than kEpsilon * kEpsilon
so you get the result where the vectors are not the same.
So, you'll need to compare the vectors in some other way. As an option, you could try Mathf.Approximately(float a, float b)
( https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html ) to compare each x, y, z
values of the vectors separately instead of using Vector3 == Vector3
.