单元测试 Mathf.Sine C#/Unity

Unit Test Mathf.Sine C# / Unity

我正在尝试写一个单元测试来检查是否有新的 vector3 returns (cos(angle), 0, sin(angle))。所以如果 pass angle = 0,它应该 returns (1,0,0).

我知道是和Episilon相关的东西,但是不知道怎么表述。

这是我的代码:

public Vector3 SetForceDirection(float angleInDegrees) 
    {
        float angleInRadians = angleInDegrees * Mathf.Deg2Rad;
        
        return new Vector3(Mathf.Cos(angleInRadians), 0, Mathf.Sin(angleInRadians));
    }

我的单元测试:

void SetforceDirection_Angle0_Return_1_0_0() 
    {
        CtrlHorizontal _ctrlHorizontal = new CtrlHorizontal();

        Vector3 result = _ctrlHorizontal.SetForceDirection(0);

        Assert.That(result == new Vector3(1, 0, 0));
    }

谢谢!

使用Debug.Log检查Mathf.Sin()/Cos() return的实际值。 Vector3.ToString() 的默认精度是点 iirc 后的 5 位数字。要在调试日志中获得更高的精度,请使用

Vector3 v = new Vector3(1.1234567891011, 0.00000001, 2.2222222222);
Debug.Log(v.ToString("F10"));

这应该打印点后 10 位数字的矢量值。

那么,到底发生了什么?
让我们以 90 度角为例。 Mathf.Cos() 的结果将是:

tmp1 = 90f * Mathf.Deg2Rad; // 1.570769
tmp2 = Mathf.Cos(tmp1); // -4.371139E-08

所以,Mathf.Cos() returns -4.37139E-08 而不是 0。原因是浮点精度限制。它打破了计算机科学中的很多东西。
Mathf.Sin()/Cos() 获得的角度为 0、90、120 的结果不会给你 0 和 1,而是给你像 4.371139E-08 这样的数字。这个值非常非常接近于零,但它不是零。因此,当您将此类结果与代码中的 new Vector 1、0 和 0 进行比较时,等式不成立。
虽然,Unity 中的 Vector3 == Vector3 操作已经应用了一些舍入,因此真正接近的值将被视为相等。与进行精确比较的 Vector3.Euals() 相反。
这是来自 Unity 源代码 repo 的 Vector3 == Vector3 操作的源代码: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;
}

如您所见,它检查向量值之间的平方差是否小于 kEpsilon * kEpsilonkEpsilon = 0.00001,因此平方值为 0.000000001)。
这里比较棘手的是Mathf.Cos()的90度可能会return0.00001,而==操作会比较0.00001 * 0.00001 < kEpsilon * kEpsilon,会return假,因为这些值实际上是一样的,所以运算结果

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 不小于 kEpsilon * kEpsilon 所以你得到向量不相同的结果。

因此,您需要以其他方式比较向量。作为一种选择,您可以尝试 Mathf.Approximately(float a, float b) ( https://docs.unity3d.com/ScriptReference/Mathf.Approximately.html ) 分别比较向量的每个 x, y, z 值,而不是使用 Vector3 == Vector3.