Infinity equivalence bug in Single, Double types

G

Guest

These assertions fail under the .NET framework 1.1. The correct behavior should be for all of them to succeed (as correctly documented in the MSDN library and as specified by the IEEE floating point math standard)

Debug.Assert(Single.PositiveInfinity != Single.PositiveInfinity)
Debug.Assert(Single.NegativeInfinity != Single.NegativeInfinity)
Debug.Assert(1.0f/0.0f != 1.0f/0.0f)

Debug.Assert(Double.PositiveInfinity != Double.PositiveInfinity)
Debug.Assert(Double.NegativeInfinity != Double.NegativeInfinity)
Debug.Assert(1.0/0.0 != 1.0/0.0)
 
J

JD

The single and doubles are structures. Can you compare two single structures
with the equality operator?

AndyP said:
These assertions fail under the .NET framework 1.1. The correct behavior
should be for all of them to succeed (as correctly documented in the MSDN
library and as specified by the IEEE floating point math standard).
 
J

Jon Skeet [C# MVP]

JD said:
The single and doubles are structures. Can you compare two single structures
with the equality operator?

Yes, but that fails in exactly the same way. Basically NaN/Infinity
values are meant to be handled differently from normal values.
Interestingly, Double.NaN displays the correct behaviour on ==, but the
incorrect behaviour for .Equals:

using System;

class Test
{
static void Main()
{
double d1 = Double.NaN;
double d2 = Double.NaN;
Console.WriteLine (d1.Equals(d2));
Console.WriteLine (d1==d2);
}
}

prints

True
False
 
J

JD

The single and doubles are structures. Can you compare two single
structures
Sorry, I wasn't clear. I was actually questioning his use of the not equal
operator against structures.

MSDN documentation says:
Use IsNaN to determine whether a value is not a number. It is not possible
to determine whether a value is not a number by comparing it to another
value equal to NaN.
 
J

Jon Skeet [C# MVP]

JD said:
Sorry, I wasn't clear. I was actually questioning his use of the not equal
operator against structures.

MSDN documentation says:
Use IsNaN to determine whether a value is not a number. It is not possible
to determine whether a value is not a number by comparing it to another
value equal to NaN.

But the point is that != *should* return true, even though both sides
are Single.PositiveInfinity or whatever.

Yes, you should use IsNaN/IsInfinity or whatever if you want to
determine whether or not a number is infinite - but the ==/!= operators
should work as intended (and as specified by IEEE) as well.
 
J

JD

I believe in AndyP's example he was comparing all Single and Double
Structures, no native types involved.

In your example where you used natives from the start in the form of d1 and
d2:

Console.WriteLine (d1.Equals(d2));
Boxed and Structure comparision, basically doing the structure
Double.Equal where this checks for Nan equality

Console.WriteLine (d1==d2);
native double check (ceq)

So it seems the native type works fine but the Structure Double.Equal does
not follow the IEEE standard. Does the question turn to, does/should the
structure Double follow the IEEE standard?

On a side note, I remember Richter warning about behind the scenes boxed
scenarios....
 
J

Jon Skeet [C# MVP]

JD said:
I believe in AndyP's example he was comparing all Single and Double
Structures, no native types involved.

The Single and Double structures *are* the native types though. What
difference are you talking about?
In your example where you used natives from the start in the form of d1 and
d2:

Console.WriteLine (d1.Equals(d2));
Boxed and Structure comparision, basically doing the structure
Double.Equal where this checks for Nan equality

It's certainly boxed, yes...
Console.WriteLine (d1==d2);
native double check (ceq)

So it seems the native type works fine but the Structure Double.Equal does
not follow the IEEE standard.

No, the native type's == operator works fine *only* for NaN, not for
the infinities, which the OP believes should follow the same rules.
Does the question turn to, does/should the
structure Double follow the IEEE standard?

Again, I don't see the difference between the Double structure and the
"native type" you're talking about.
 
J

JD

Oops my bad. Let me start from a clean slate.

Looking at the annotated standard. There is a difference between equality
and equivalence. The Equals method follows equivalence and should return
true for d1.Equals(d2). d1 == d2 should not. Right or wrong this is how they
meant it. Your example seems to hit the behavior they were looking for.

Now in AndyP example:

Console.WriteLine(Double.PositiveInfinity == Double.PositiveInfinity);
Console.WriteLine(Double.NegativeInfinity == Double.NegativeInfinity);
Console.WriteLine(1.0/0.0 == 1.0/0.0);

Outputs to IL:

IL_005e: ldc.i4.1
IL_005f: call void [mscorlib]System.Console::WriteLine(bool)
IL_0064: ldc.i4.1
IL_0065: call void [mscorlib]System.Console::WriteLine(bool)
IL_006a: ldc.i4.1
IL_006b: call void [mscorlib]System.Console::WriteLine(bool)
Are constants comparisons held to the same IEEE standard? If so then its a
bug in the compiler.
 
E

Elder Hyde

There's no such thing as a native type in C#. When you say double, it IS
System.Double. They're just C# aliases for CLR types.
I believe in AndyP's example he was comparing all Single and Double
Structures, no native types involved.

In your example where you used natives from the start in the form of d1 and
d2:

Console.WriteLine (d1.Equals(d2));
Boxed and Structure comparision, basically doing the structure
Double.Equal where this checks for Nan equality

Console.WriteLine (d1==d2);
native double check (ceq)

So it seems the native type works fine but the Structure Double.Equal does
not follow the IEEE standard. Does the question turn to, does/should the
structure Double follow the IEEE standard?

On a side note, I remember Richter warning about behind the scenes boxed
scenarios....
 
J

Jon Skeet [C# MVP]

JD said:
Oops my bad. Let me start from a clean slate.

Looking at the annotated standard. There is a difference between equality
and equivalence. The Equals method follows equivalence and should return
true for d1.Equals(d2). d1 == d2 should not. Right or wrong this is how they
meant it. Your example seems to hit the behavior they were looking for.

Now in AndyP example:

Console.WriteLine(Double.PositiveInfinity == Double.PositiveInfinity);
Console.WriteLine(Double.NegativeInfinity == Double.NegativeInfinity);
Console.WriteLine(1.0/0.0 == 1.0/0.0);

Outputs to IL:

IL_005e: ldc.i4.1
IL_005f: call void [mscorlib]System.Console::WriteLine(bool)
IL_0064: ldc.i4.1
IL_0065: call void [mscorlib]System.Console::WriteLine(bool)
IL_006a: ldc.i4.1
IL_006b: call void [mscorlib]System.Console::WriteLine(bool)
Are constants comparisons held to the same IEEE standard? If so then its a
bug in the compiler.

I think they should be, so yes, there's a bug in the compiler - but I
tested it without that, too:

double d1 = Double.PositiveInfinity;
double d2 = Double.PositiveInfinity;

Console.WriteLine (d1==d2);

This still prints true, and shouldn't. In other words, it's treating
infinity as different to NaN, which I don't *think* it should do.
 
J

Jon Skeet [C# MVP]

There's no such thing as a native type in C#. When you say double, it IS
System.Double. They're just C# aliases for CLR types.

Well, there are types which return true for Type.IsPrimitive. They are
types the CLR knows about directly, rather than just as custom
structures.
 
J

JD

Thank you. I couldn't say it better. Doubles are float64 in the CLR. I think
it may be time to start digging around Rotor a little...
 
A

AndyP

The less-than/greater-than-or-equal to operators also are incorrect when
comparing infinities.
// These assertions fail

Debug.Assert(!(1.0/0.0 <= Double.PositiveInfinity));

Debug.Assert(!(1.0/0.0 >= Double.PositiveInfinity));

Debug.Assert(!(-1.0/0.0 <= Double.NegativeInfinity));

Debug.Assert(!(-1.0/0.0 >= Double.NegativeInfinity));

// These pass

Debug.Assert(!(1.0/0.0 < Double.PositiveInfinity));

Debug.Assert(!(1.0/0.0 > Double.PositiveInfinity));

Debug.Assert(!(-1.0/0.0 < Double.NegativeInfinity));

Debug.Assert(!(-1.0/0.0 > Double.NegativeInfinity));

AndyP said:
These assertions fail under the .NET framework 1.1. The correct behavior
should be for all of them to succeed (as correctly documented in the MSDN
library and as specified by the IEEE floating point math standard).
 
E

Elder Hyde

By native type, I meant really primitive ones like the ones in Java,
where they draw quite a clear line between the native types and the
wrapper types.

Type.IsPrimitive, to me, just tells me whether the CLR knows about a
type or not, but it doesn't tell me whether I can treat this type like a
structure (e.g.: call methods on it etc.), or it is really native (or
primitive? One can debate over this choice of word I suppose). But
everything really is a structure, isn't it?
 
J

Jon Skeet [C# MVP]

By native type, I meant really primitive ones like the ones in Java,
where they draw quite a clear line between the native types and the
wrapper types.

Type.IsPrimitive, to me, just tells me whether the CLR knows about a
type or not, but it doesn't tell me whether I can treat this type like a
structure (e.g.: call methods on it etc.), or it is really native (or
primitive? One can debate over this choice of word I suppose). But
everything really is a structure, isn't it?

No, it isn't. There are value types and reference types. All the
primitive types other than string are value types. However, all value
types can also be boxed, and the boxed type is a reference type.

You can call methods on everything in .NET. For instance:

int x = 10;
Console.WriteLine (x.ToString());
 
E

Elder Hyde

Er, I didn't qualify myself properly. I mean, everything that looks like
a primitive type (bool, int32, single, etc.) is actually a structure,
isn't it? (I know there are reference types)

I've always thought that boxing in .NET is just syntactical sugar--not
much goes under the hood because they are already a structure. (unlike
Java maybe? I guess in Java they really have to generate new
Integer(intValue) for every boxing).
 
J

Jon Skeet [C# MVP]

Er, I didn't qualify myself properly. I mean, everything that looks like
a primitive type (bool, int32, single, etc.) is actually a structure,
isn't it? (I know there are reference types)

Apart from string (which is officially a primitive type as the CLR has
special knowledge of it), they are all value types, yes.
I've always thought that boxing in .NET is just syntactical sugar--not
much goes under the hood because they are already a structure.

No, that's not true. Have a look in the ECMA spec for the box and unbox
instructions. Boxing puts a value which might otherwise be on the stack
on the heap.
(unlike Java maybe? I guess in Java they really have to generate new
Integer(intValue) for every boxing).

And that's exactly what happens in .NET too - it's just that there's
one type name which covers both the boxed and unboxed versions.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top