0

code(from interactive shell):

> var a = new Dictionary<float, string>();
> a.Add(float.NaN, "it is NaN");
> a[float.NaN]
"it is NaN"

So it is possible, but is it safe?

CDnX
  • 27
  • 1
  • 6
  • "safe" compared to what? It can compile... What results do you expect (assuming you read https://stackoverflow.com/questions/1145443/why-is-double-nan-not-equal-to-itself)? – Alexei Levenkov Dec 12 '19 at 03:04
  • 2
    Yes, it is 'safe' in context, even if somewhat unexpected. While `float.NaN == float.NaN` is *false* (per standard IEEE 754 rules), `float.NaN.Equals(float.NaN)` is true per .. reasons. (.NET Standard 3.0 changes some FP-handling rules, does that include Equal'ity?) – user2864740 Dec 12 '19 at 03:04

3 Answers3

2

That depends on what you mean by safe.

If you expect people to be able to use the dictionary and compare its keys to other floats, they will have to deal with a key value of NaN correctly themselves. And since float.NaN == float.NaN happens to be False, that may cause issues down the line.

However, the Dictionary succeeds in performing the lookup and other operations work correctly as well.

The question here is really why you need it in the first place?

Grismar
  • 27,561
  • 4
  • 31
  • 54
2

Paraphrasing from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Single.cs;

public const float NaN = (float)0.0 / (float)0.0;
public static unsafe bool IsNaN(float f) => f != f;
public int CompareTo(object? value){
   ...
   if (m_value < f) return -1;
   if (m_value > f) return 1;
   if (m_value == f) return 0;
   if (IsNaN(m_value))
      return IsNaN(f) ? 0 : -1;
   else // f is NaN.
      return 1;
}
public bool Equals(float obj)
{
   if (obj == m_value)
   {
      return true;
   }
   return IsNaN(obj) && IsNaN(m_value);
}
public override int GetHashCode()
{
   int bits = Unsafe.As<float, int>(ref Unsafe.AsRef(in m_value));
   // Optimized check for IsNan() || IsZero()
   if (((bits - 1) & 0x7FFFFFFF) >= 0x7F800000)
   {
      // Ensure that all NaNs and both zeros have the same hash code
      bits &= 0x7F800000;
   }
   return bits;
}

You can see that NaN requires special handling in each of these cases. The standard IEEE representation leaves most bits undefined, and defines special cases for comparisons even if those bit values are identical.

However you can also see that both GetHashCode() && Equals() treat two NaN's as equivalent. So I believe that using NaN as a dictionary key should be fine.

Jeremy Lakeman
  • 9,515
  • 25
  • 29
  • As https://stackoverflow.com/a/59297194/477420 reminded floats are awful keys to start with... so realistically whatever behavior of NaN comparison is using floats pretty much guarantees that other keys are never found (unless one uses constant values only...) Since one would pretty much always use custom comaprer (like https://stackoverflow.com/questions/48655003/how-to-use-a-float-as-key-in-c-sharp-dictionary-with-custom-comparer-that-rounds) for float dictionary anyway it is easy to add whatever equality you like to NaN rather than rely on looking at source code. – Alexei Levenkov Dec 12 '19 at 06:50
  • Right, other than -0 and Nan, the default implementation requires floats with identical representation. I wouldn't recommend it, but it does work. – Jeremy Lakeman Dec 13 '19 at 00:23
1

It's bad idea to use float as key of Dictionary.

In theory you can do it. But when you work with float\double\decimal you shoud use some Epsilon to compare 2 values. Use formula like this:

abs(a1 - a2) < Epsilon

It's need due to rounding of float in operations and existing of irrational numbers. For example how you will compare with PI or sqrt(2)?

So, on this case using float as dictionary key is bad idea.

TemaTre
  • 1,422
  • 2
  • 12
  • 20
  • Yes, float as key is bad idea... But it is different question so... and while "don't do that" is somewhat an answer... – Alexei Levenkov Dec 12 '19 at 06:46
  • Off cause. But Threre are 2 correct answers about NaN and Dictionary and .equals method. But nobody write: If you so choose, you can still shoot yourself in the foot (C) – TemaTre Dec 12 '19 at 07:11