为什么这个表达式的结果是False?

13

我有点困惑,无法解释这种行为:

Vector3 k = new Vector3(Mathf.NegativeInfinity, Mathf.NegativeInfinity,Mathf.NegativeInfinity);
Debug.Log(k==k); // evaluates to False

尽管
Debug.Log(Mathf.Mathf.NegativeInfinity == Mathf.Mathf.NegativeInfinity)
// evaluates to True as expected

我正在使用Unity 5.3.5f1版本。


尝试使用IL浏览器查看重载的==运算符内部。 - GSerg
3个回答

8
从Unity的文档来看,==返回“对于非常接近相等的向量为真”。然而,当Vector以负无穷大的x、y、z初始化时,这种实现会产生问题。
让我们看一下如何为Vector3定义==:
public static bool operator == (Vector3 lhs, Vector3 rhs) {
    return Vector3.SqrMagnitude (lhs - rhs) < 9.999999E-11;
}

在执行SqrMagnitude之前,会先执行lhs - rhs,因此让我们看一下-的定义:
public static Vector3 operator - (Vector3 a, Vector3 b) {
    return new Vector3 (a.x - b.x, a.y - b.y, a.z - b.z);
}

对于普通数字,这是可以的,但是由于a.x、b.x等都是Mathf.NegativeInfinity,减法的结果将会是NaN。现在当它执行sqrMagnitude时:

public float sqrMagnitude {
    get {
        return this.x * this.x + this.y * this.y + this.z * this.z;
    }
}

这也会返回NaN
文档中,我们注意到以下内容:
  • 如果任一操作数为NaN,则对于除!=外的所有运算符,结果均为false;对于!=,结果为true。
因此,当我们回到这段代码时:
return Vector3.SqrMagnitude (lhs - rhs) < 9.999999E-11;

简化后的代码为return NaN < 9.999999E-11;,根据文档所述将返回False


Debug.Log(Mathf.Mathf.NegativeInfinity == Mathf.Mathf.NegativeInfinity)的行为符合预期的原因在这里有说明。

  • 负零和正零被认为是相等的。
  • 负无穷小被认为比所有其他值都小,但与另一个负无穷小相等。
  • 正无穷大被认为比所有其他值都大,但与另一个正无穷大相等。

2

等值运算符可以实现,也可以不实现。这仅仅是给定类型的实现细节。或者它也可能被错误地实现。

即使在比较两个引用时,给定类的所有属性都相等,如果没有重载==!=,或者它们的实现是错误的,那么结果可能会出乎意料,就像你的情况一样。

例如:

public class A 
{
     public static operator bool ==(A left, A right) => false;
     public static operator bool !=(A left, A right) => false;
}

A a = new A();

bool equals = a == a; // false
bool notEquals = a != a // false

顺便说一下:
bool referenceEquals = ReferenceEquals(a, a); // TRUE!

"等号运算符可以实现,也可以不实现。这只是给定类型的实现细节。或者它也可能被错误地实现。" 这就是问题所在。==重载运算符被实现为测试近似相等性。" - Programmer
1
@程序员 是的,毕竟这只是一个实现细节。楼主认为 == 总是意味着“绝对相等”。 - Matías Fidemraizer

2

Mathf.NegativeInfinity不是一个实际的数字,它只是-Infinity的表示。根据文档

负无穷大的表示(只读)。

使用Mathf.NegativeInfinity作为x、y、z分量初始化Vector3将不起作用。如果您尝试打印此向量,您将得到(-Infinity, -Infinity, -Infinity)而不是任何数字。

运行一些测试表明,在Vector3中,float.MaxValue是能够正常工作的最大值。

正如Matías在他的答案中所说关于=操作符。我相信这在Vector3类中也是正确的。使用Equals方法也可以。

以下是示例代码:

void Start () 
{
    Vector3 k = new Vector3(Mathf.NegativeInfinity, Mathf.NegativeInfinity,Mathf.NegativeInfinity);
    bool val = k==k;
    Debug.Log("Operator on Infinity Vector3: " + val);
    Debug.Log(k);
    Debug.Log("Equals Method on Infinity Vector3: " + k.Equals(k));

    val = (Mathf.NegativeInfinity == Mathf.NegativeInfinity);
    Debug.Log("Operator on float value: " + val);

    k = new Vector3(float.MaxValue, float.MaxValue,float.MaxValue);

    val = k==k ;
    Debug.Log("Operator on float.MaxValue: " + val);
    Debug.Log(k);
    Debug.Log("Equals Method on float.MaxValue: " + k.Equals(k));

}

上述代码的结果如下:

无限向量3的运算:False

(负无穷,负无穷,负无穷)

无限向量3的等于方法:True

浮点数值的运算:True

浮点数最大值的运算:True

(340282300000000000000000000000000000000.0, 340282300000000000000000000000000000000.0, 340282300000000000000000000000000000000.0)

浮点数最大值的等于方法:True


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接