网络知识 娱乐 DOTNET 7 中关于 NaN 相等性比较的调整

DOTNET 7 中关于 NaN 相等性比较的调整

比如之前写过一篇文章 “深入理解C#中的相等性比较” ,对C#(DOTNET)中的相等性比较进行了系统梳理和介绍。

在文章中,提到在浮点数中,有一个非数 NaN(Not a Number),从数学意义上来说,两个 NaN 是不相等的。因此 float 和 double 重写了 == 运算符,让 NaNNaN 不相等。但在约定中, object.Equals 方法必须保证自己等于自己,因此两者的行为出现了不一致。

float f = float.NaN;n Console.WriteLine(f == f); // Falsen Console.WriteLine(f.Equals(f)); // True

上述代码中, Equals 方法按照自身必须和自身相等约定返回 True。

== 运算则遵循了 IEEE 754 的要求,认为 NaN 不等于自身,其实从语义上来说,这是很合理的,NaN 只是表明值不能用数字来表示,不能用数字表示的情况有很多,相互之间肯定不能算相等。

但这只是理论上。在实践中,NaN 不等于自身的行为,会对矩阵、向量、集合、字典等类型数据结构行为的影响是比较大的,比如。

Vector2 v = new Vector2(float.NaN);n Console.WriteLine(v == v); // Falsen Console.WriteLine(v.Equals(v)); // False

上面的代码中,由 float.NaN 构造的 Vector2 与自身不相等。虽然 NaN 不定于 NaN 从数学上说是很自然的,但由此构造的别的对象互相不相等的合理性确值得商榷。

当然,这还不是致命的,如果作为字典类数据结构的健,该类型的健将无法被解析,导致数据丢失。

Vector2 v = new Vector2(float.NaN);n var s = new HashSet<Vector2>();n s.Add(v);n Console.WriteLine(s.Contains(v)); // False,数据丢失

对此,.NET 7 做了调整,从此 NaN == NaN 返回 True,这违反了 IEEE 754 的要求,但在实践中更加实用,而且若不考虑严格的数学意义,使用起来也是相当直观和可接受的。

float f = float.NaN;n Console.WriteLine(f == f); // Truen Console.WriteLine(f.Equals(f)); // True

这是一个破坏性的改变,该改变会影响以下类的相等性比较,如果有相关的代码,升级 .NET 7 时务必要进行充分测试。

  • System.Numerics.Matrix3x2.Equals
  • System.Numerics.Matrix4x4.Equals
  • System.Numerics.Plane.Equals
  • System.Numerics.Quaternion.Equals
  • System.Numerics.Vector2.Equals
  • System.Numerics.Vector3.Equals
  • System.Numerics.Vector4.Equals
  • System.Numerics.Vector.Equals
  • System.Runtime.Intrinsics.Vector64.Equals
  • System.Runtime.Intrinsics.Vector128.Equals
  • System.Runtime.Intrinsics.Vector256.Equals