NaN(非数字), PositiveInfinity(无穷大), NegativeInfinity(无穷小)
解释: NaN = Not a number
读书时发现这个有趣的问题, 我们用一段测试代码来开始:
1: using System;
2: using System.Collections.Generic;
3:
4: namespace Roger.Testing
5: {
6: public class Testing
7: {
8: public static void Main()
9: {
10: double a = 3.1415926;
11: double b = 3.1415926;
12:
13: Console.WriteLine("<- Comparing Values ->");
14: Console.WriteLine("Equals: {0}", a.Equals(b));
15: Console.WriteLine("==: {0}", a == b);
16:
17: Console.WriteLine("<- Comparing NaNs ->");
18: double c = double.NaN;
19: double d = double.NaN;
20: Console.WriteLine("Equals: {0}", c.Equals(d));
21: Console.WriteLine("==: {0}", c == d);
22:
23: Console.WriteLine("<- Comparing PositiveInfinity ->");
24: double e = double.PositiveInfinity;
25: double f = double.PositiveInfinity;
26: Console.WriteLine("Equals: {0}", e.Equals(f));
27: Console.WriteLine("==: {0}", e == f);
28:
29: Console.Read();
30: }
31: }
32: }
结果如下:
我们看到NaN和NaN之间比较的结果是不一样的,但是为什么呢?都是NaN, 而且Equals和==结果不一样,关于这个的问题,在ECMA中有过特别的注明
Although two floating point NaNs are defined by IEC 60559:1989 to always compare as unequal, the System.Object.Equals requires that overrides must satisfy the requirements for an equivalence contract for operator. Therefore, System.Double.Equals and System.Single.Equals return True when comparing two NaNs, while the equality operator returns False in that case, as required by the IEC standard.
虽然在IEC 60559:1989中定义两个NaN浮点数不相等,但是System.Object.Equals需要的重写必须满足操作的相等条件(an equivalence contract for operator)。所以, System.Double.Equals 和 System.Single.Equals 在比较连个NaNs返回True, 而相等操作符就遵循IEC标准返回False.
注: an equivalence contract for operator 代表
1. a == a // true
2. if a == b then b == a
3. if a == b and b == c then a == c
在这段话中,意为NaN和NaN对于CLS来说都是一致的,所以应该相等,这个本身是.Net特定环境的一个规则,对于其他语言并不一致
如果您看这个有点迷糊的时候,再看看Object中对于Equals中的实现(来自SSCLI20/CLR/BCL/System/Double.cs).
1:
2: // True if obj is another Double with the same value as the current instance. This is
3: // a method of object equality, that only returns true if obj is also a double.
4: public override bool Equals(Object obj) {
5: if (!(obj is Double)) {
6: return false;
7: }
8: double temp = ((Double)obj).m_value;
9: // This code below is written this way for performance reasons i.e the != and == check is intentional.
10: if (temp == m_value) {
11: return true;
12: }
13: return IsNaN(temp) && IsNaN(m_value);
14: }
而IsNaN的实现则更加巧妙,大家一起欣赏一下
1: public static bool IsNaN(double d)
2: {
3: //Jit will generate inlineable code with this
4: // warning CS1718: comparison to same variable
5: #pragma warning disable 1718
6: if (d != d)
7: {
8: return true;
9: }
10: else
11: {
12: return false;
13: }
14: #pragma warning restore 1718
15: }
而d != d代表什么意思呢,我们看看NaN的具体值
1: public const double NegativeInfinity = (double)-1.0 / (double)(0.0);
2: public const double PositiveInfinity = (double)1.0 / (double)(0.0);
3: public const double NaN = (double)0.0 / (double)0.0;
顺带我们也看了看PositiveInfinity和NegativeInfinity的值,也许你会觉得不可思议,为什么这样的值也是可以运行通过的,我们再深入一下,查看之前ECMA提到的IEEE标准: http://en.wikipedia.org/wiki/IEEE_754
在学计算机原理的时候,相信老师们都已经讲到了浮点数的表示方法,抱歉我也忘的差不多了, 我从IEEE标准中截取了对于NaN, PositiveInfinity和NegativeInfinity的定义,大家看看
- if exponent is 0 and fraction is 0, the number is ±0 (depending on the sign bit)
- if exponent = 2e − 1 and fraction is 0, the number is ±infinity (again depending on the sign bit), and
- if exponent = 2e − 1 and fraction is not 0, the number being represented is not a number (NaN).
对于NaN来说后面23位并不一致,而±infinity则都为0. 于是本文之初的结果现在看起来也是那么的自然了. :)
顺道提一下.Net对于三者之间的运算方法(来自 ECMA-335/ Virtual Execution System)
X rem 0 = NaN // X 除以 0 = NaN
0 * +infinity = 0 * -infinity = NaN
(X / 0) = +infinity, if X > 0
NaN, if X = 0
infinity, if X < 0
NaN op X = X op NaN = NaN for all operations
(+infinity) + (+infinity) = (+infinity)
X / (+infinity) = 0
X mod (-infinity) = -X
(+infinity) - (+infinity) = NaN
Have Fun, :)