C#中的零(-0)等于零(0)

C#中的零(-0)等于零(0)?

对于整数,没有二进制表示在0和-0之间产生差异,因此它们在定义上是相等的。

对于IEEE浮点数,存在负零和正零的区别。 我做了一些测试(.NET Framework 2.0的CLR,C#3),似乎它们被认为是相同的,这实际上是根据IEEE 754标准预期的行为。

这是我的测试代码,用于显示:

double minusOne = -1.0; double positiveZero = 0.0; double negativeZero = minusOne*positiveZero; Console.WriteLine("{0} == {1} -> {2}", positiveZero, negativeZero, positiveZero == negativeZero); Console.WriteLine("Binary representation is equal: {0}", BitConverter.DoubleToInt64Bits(positiveZero) == BitConverter.DoubleToInt64Bits(negativeZero)); 

返回:

 0 == 0 -> True Binary representation is equal: False 

对于小数,至少有4种类型的零:

 Decimal zero = Decimal.Zero; Decimal negativeZero1 = new Decimal(0, 0, 0, true, 0); Decimal negativeZero2 = -0.0m; Decimal negativeZero3 = Decimal.Negate(Decimal.Zero); 

虽然所有都相等并打印为"0" ,但它们具有不同的位表示:

 zero: {0x00000000 00000000 00000000 00000000 } negativeZero1: {0x00000000 00000000 00000000 80000000 } negativeZero2: {0x00000000 00000000 00000000 80010000 } negativeZero3: {0x00000000 00000000 00000000 80000000 } 

来源: 十进制负零表示

 if (-0 == 0) Console.WriteLine("YES, it is!"); 

听起来你正在寻找它们不可互换的边缘情况,所以这里有一些例子。

object.Equals on struct s

 > struct SomeStruct { public double X; } > var a = new SomeStruct { X = 0d }; > var b = new SomeStruct { X = -0d }; > a.Equals(b) false > 

逆温

 > 1/0d ∞ > 1/-0d -∞ > 

显式字节转换

当然,任何类型的显式字节或按位解构,或类型惩罚。 这些例子来自PC:

 > BitConverter.GetBytes(0d) byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 } > BitConverter.GetBytes(-0d) byte[8] { 0, 0, 0, 0, 0, 0, 0, 128 } > 

Math.Sign

尽管你可能会有所期待,但Math.Sign没有区分负零和正零。 它只会告诉您数字是等于,大于还是小于0。

 > Math.Sign(-0f) 0 

Math.MinMath.Max

一些算术运算具有-0的边缘情况。 一个有趣的是Math.Min (并且等效于max),这是在比较带符号的零时它返回第二个。 所以:

 > BitConverter.GetBytes(Math.Min(0.0, -0.0)) byte[8] { 0, 0, 0, 0, 0, 0, 0, 128 } > BitConverter.GetBytes(Math.Min(-0.0, 0.0)) byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 } > 

关于不规则decimal表示的注释

decimal类型本身没有负0,但它确实有零的多个二进制表示:

 > new decimal(new int[4] { 0, 0, 0, 0 }) 0 > new decimal(new int[4] { 0, 0, 0, -2147483648 }) 0 > 

第二个例子可以被认为是负零,因为它与常规零点按位相同,除非设置了否定位。 但就格式化程序而言,它只是零。 事实上,对于不同的小数点移位,有十几decimal数表示零,所有这些都是算术等价的,并显示为0:

 > new decimal(new int[4] { 0, 0, 0, 131072 }) 0.00 > new decimal(new int[4] { 0, 0, 0, 1835008 }) 0.0000000000000000000000000000 > new decimal(new int[4] { 0, 0, 0, 65536 }) 0.0 

也就是说,您只能通过二进制比较或二进制转换方式将它们彼此区分开来。 从实验开始,似乎上面的struct技巧对它们不起作用。 Math.Min返回第二个给出的零。

奖励:次正常数字

floatdouble类型中的某些位模式表示次正规 (又称非正规)值。 我不会进入它们的位置 – 而是看到链接 – 但重要的是要知道CLI规范明确地声明它们的操作是特定于实现的。 我不知道有平台将它们视为0,但可能存在。 另一方面, C#编程语言表示它们被“视为有效的非零值”。