DeprecatedDoubleOrdering

#1

[warn] /xxx.scala:59:47: object DeprecatedDoubleOrderin
g in object Ordering is deprecated (since 2.13.0): There are multiple ways to order Doubles (Ordering.Double.Tot
alOrdering, Ordering.Double.IeeeOrdering). Specify one by using a local import, assigning an implicit val, or pa
ssing it explicitly. See the documentation for details.

What documentation?

#2

Of DeprecatedDoubleOrdering and/or the other two.

#3

OK, let me try again. Where can I find the documentation that I need to solve this problem? How would I use an implicit val to solve this problem? How would I use a local import to solve this problem? How would I “pass it explicitly” to solve this problem? And which of the three is the best solution? Finally, why is this even a problem? I’ve never had to worry about ordering floating point numbers before. I really have more important things to do than this, and I am disappointed that Scala is making me waste my time on it. Thanks.

#4

You can find the documentation here: https://www.scala-lang.org/api/2.13.0/scala/math/Ordering$$Double$.html

FWIW the deprecation message will be a bit clearer in the next version.

#5

Thanks for the reply, but I had already found that page and it doesn’t answer my question. What hack do I have to put into my code to overcome this problem?

I really shouldn’t have to waste my time figuring out how to explicitly specify a method for comparing floating point numbers. I think the Scala team fumbled the ball big time on this one.

Is this problem about how to deal with NaN or some such thing? If there is some controversy about the best method, at least provide one that works by default and let the user select another one if he cares. I honestly don’t care which one is used. If I have an NaN, I have a bigger problem than how it is sorted!

#6

original design discussion is at https://github.com/scala/scala/pull/6410

PR with tweaked deprecation message, merged for 2.13.1: https://github.com/scala/scala/pull/8191

1 Like
#7

Sorry if I am being dense, but will someone please just give me an example of how to deal with thos DeprecatedDoubleOrdering problem? I mean a code snippet. Thanks.

#8

As it implies in the error message, you need to explicitly specify the Ordering that you want to use, either:

implicit val order = Ordering.Double.TotalOrdering

or

implicit val order = Ordering.Double.IeeeOrdering

I believe you have to do this explicitly because they are both technically legal, but they produce different results because of the sheer weirdness of NaN. There isn’t a clearly-correct default, so you must choose one.

#9

Thanks, but I still don’t understand why one of these two options can’t just be made the default while letting the user choose the other one if desired. I really don’t care which one. This seems to me like the kind of aggravation that could turn some off to Scala. Or is it perhaps just my coding style that triggered this warning?

#10

Which one? Seriously – it matters if you’re serious about the details, and it’s unobvious which to use, because the behavior is different at the edge cases. So the stdlib decided that you have to decide this explicitly for your application, rather than choosing one arbitrarily (and perhaps incorrectly); that makes sense to me…

#11

OK well… not your responsibility to educate me, but I’ve been doing significant number crunching for a long time, and this issue is new to me. I have not yet been able to find anything that clearly explains the difference between the two options. If you can direct me to such an explanation, please do so.

Again, If I understand correctly, the only difference occurs when NaN is involved, but that is an error case. When I see NaN, I know something is wrong, and the correct choice of ordering method is unlikely to correct it.

#12

You don’t have to choose one or the other, but if you don’t, you receive the warning about the possible issues.

#13

Yeah… if I want to drown in warning messages every time I compile! Actually, it occurred to me that perhaps I should choose the one that will minimize the damage if I take the square root of a negative number. But until I know what the actual difference is between the two, I can’t even make that choice.

#14

I don’t claim to be an expert on this, but it seems to me that, rather than selecting an ordering method, it would make more sense to choose whether or not to have NaN (or Infinity) throw an exception. I must admit I don’t understand why they don’t throw an exception by default. My experience is that I usually could have saved some debugging time if I had just been alerted immediately that I had an NaN.

#15

The problem is as follows. Suppose you have Seq(1.0, NaN, -1.0). You want to take the min, the max, and return the sorted version, all using the same ordering.

Well, the min should clearly be NaN because you always get NaN if you try operations on data with NaN. And the max should also clearly be NaN for the same reason.

However, you’d expect xs.sorted.head to be the same as min, so sorting clearly has to put NaN first. Also, xs.sorted.last should be the same as max, so sorting as to put NaN last. So sorting is…not a sort any more.

This is all kinds of weird. Very reasonable assumptions break if you accept both that sorting doubles is possible, and if normal IEEE NaN-eats-everything rules apply.

If you want sort order to agree with min and max, then you choose TotalOrdering, and accept that max will get NaN but min will avoid it (ranking NaN as larger than PositiveInfinity).

If you don’t care that sort order disagrees with min and max, you just want your NaNs like IEEE says, then you choose IeeeOrdering.

https://www.scala-lang.org/api/2.13.0/scala/math/Ordering$$Double$$IeeeOrdering.html and https://www.scala-lang.org/api/2.13.0/scala/math/Ordering$$Double$$TotalOrdering.html explain what they do, but not about the inherent conflict in more than minimal detail.

#16

Thanks for the explanation, but I don’t think either alternative is correct. If we have Seq(1.0, NaN, -1.0), there are two possible correct alternatievs as I see it.

Alternative 1: min and max are both undefined and throw an exception.
Alternative 2: NaN is ignored, so the min is -1.0 and the max is 1.0.

As for comparisons, asking whether any number is greater than (or less than) NaN should also be undefined and should throw an exception. The very name NaN tells us that it isn’t a number, so how can it correctly be compared to a number? It can’t. If the programmer wants to handle it some particular way, he can catch the exception and deal with it appropriately – or correct the error that caused the NaN to start with.

I suggest those rules be implemented in Scala and called ScalaOrdering.

#17

As for comparisons, asking whether any number is greater than (or less than) NaN should also be undefined and should throw an exception.

That’s questionable. NaN may not be a valid number, but it is a valid inhabitant of Double. If you want to impose a partial ordering that excludes NaN, min and max become undefined, and should be something like partialMin and partialMax instead. Then they would return a set of roots that are not <= any other element in the set

You can also filter the values you don’t want to think about – the NaN values – out before imposing such an ordering over them.

#18

Your suggestion to throw exceptions results in code that compiles but fails at runtime, possibly on rare untested inputs. This is one of the least graceful ways to handle things.

Also, maybe you don’t like IEEE754 floating point very much, but you’re explicitly contradicting the rules it has for comparisons. If you want to read about the history and rationale for the rules, the top-voted answer to https://stackoverflow.com/questions/1565164 is pretty good.

Regardless, you are free to implement your own Ordering[Double] that throws exceptions, and you can bring it into scope as an implicit, if that’s what is called for in your application.

#19

If the list contains NaN, then min and max should also be NaN. Unfortunately, I don’t think you can provide an Ordering that does that.

#20

Yes, NaN is a “valid inhabitant of Double,” but in principle it shouldn’t be. In principle, floats should be an Option, with NaN as None, but that would be more complicated and less efficient, so we make do with NaN. If we used Option, a comparison of a number with None would not compile, but since we don’t do it that way, an exception is the next best thing.

Exceptions are undesirable in production runs and field operations, of course, but they can be very helpful for testing. If NaN threw an exception, there could be a compiler switch to turn NaN exceptions on and off just as there is for “assert.”