Description
(Reposted from github.com/fsharp/fsharp)
I know that nan = nan
and nanf = nanf
both return false
.
But nan.Equals(nan)
and nanf.Equals(nanf)
return true
. These are intended results and this also applies when nan
or nanf
is contained in another structural type such as a union.
The .NET Guidelines also say that Equals
, GetHashCode
and CompareTo
should be consistent. That is, two values that are equal should be equal as per Equals
, should return the same hash and CompareTo
should return 0
.
F# types adhere to this except for the following case when a single
/float32
or double
/float
that is NaN is a value of a structural type such as a union, which is nested in another structural type such as a union or record that has at least one type parameter.
Here is sample code that shows the inconsistent behavior:
type UnionWithFloat = UnionWithFloat of float
type GenericUnion<'value> = GenericUnion of 'value
type NonGenericUnion = NonGenericUnion of UnionWithFloat
let leftGenericUnion = GenericUnion (UnionWithFloat nan)
let rightGenericUnion = GenericUnion (UnionWithFloat nan)
let leftNonGenericUnion = NonGenericUnion (UnionWithFloat nan)
let rightNonGenericUnion = NonGenericUnion (UnionWithFloat nan)
let comparableLeftGenericUnion = leftGenericUnion :> System.IComparable<GenericUnion<UnionWithFloat>>
let comparableLeftNonGenericUnion = leftNonGenericUnion :> System.IComparable<NonGenericUnion>
let genericUnionsEqual = leftGenericUnion.Equals(rightGenericUnion) // false!
let nonGenericUnionsEqual = leftNonGenericUnion.Equals(rightNonGenericUnion) // true
let genericUnionsHashCodesEqual = leftGenericUnion.GetHashCode() = rightGenericUnion.GetHashCode() // true
let nonGenericUnionsHashCodesEqual = leftNonGenericUnion.GetHashCode() = rightNonGenericUnion.GetHashCode() //true
let genericUnionsComparisonEqual = comparableLeftGenericUnion.CompareTo(rightGenericUnion) = 0 // true
let nonGenericUnionsComparisonEqual = comparableLeftNonGenericUnion.CompareTo(rightNonGenericUnion) = 0 // true
The inconsistent result is the following line:
let genericUnionsEqual = leftGenericUnion.Equals(rightGenericUnion) // false!
All the other ways of comparison correctly return true
.
Note that this also applies to single
/float32
and records. And you only see the inconsistent results when the type is generic (GenericUnion
here), which nests another type (UnionWithFloat
here). I have not tested this for more types or constellations. This was tested with F# 3.1 if this makes a difference.
I would be nice if you could fix this as it had me scratching my head for some time until I could pin down when this happens. It would also be nice if you could verify that the results are consistent for all possible types in all constellations in F#, whether generic and/or nested.
Metadata
Metadata
Assignees
Type
Projects
Status