[swift-dev] Rationalizing FloatingPoint conformance to Equatable

Xiaodi Wu xiaodi.wu at gmail.com
Tue Oct 31 21:34:32 CDT 2017


On Tue, Oct 31, 2017 at 11:07 AM, Stephen Canon <scanon at apple.com> wrote:

> [Replying to the thread as a whole]
>
> There have been a bunch of suggestions for variants of `==` that either
> trap on NaN or return `Bool?`. I think that these suggestions result from
> people getting tunnel-vision on the idea of “make FloatingPoint equality
> satisfy desired axioms of Equatable / Comparable”. This is misguided. Our
> goal is (should be) to make a language usable by developers; satisfying
> axioms is only useful in as much as they serve that goal.
>
> Trapping or returning `Bool?` does not make it easier to write correct
> concrete code, and it does not enable writing generic algorithms that
> operate on Comparable or Equatable. Those are the problems to be solved.
>
> Why do they not help write correct concrete code? The overwhelming
> majority of cases in which IEEE 754 semantics lead to bugs are due to
> non-reflexivity of equality, so let’s focus on that. In the cases where
> this causes a bug, the user has code that looks like this:
>
>         // Programmer fails to consider NaN behavior.
>         if a == b {
>         }
>
> but the correct implementation would be:
>
>         // Programmer has thought about how to handle NaN here.
>         if a == b || (a.isNaN && b.isNaN) {
>         }
>
> W.r.t ease of writing correct *concrete* code, the task is to make *this*
> specific case cleaner and more intuitive. What does this look like under
> other proposed notions of equality? Suppose we make comparisons with NaN
> trap:
>
>         // Programmer fails to consider NaN behavior.  This now traps if a
> or b is NaN.
>         // That’s somewhat safer, but almost surely not the desired
> behavior.
>         if a == b {
>         }
>
>         // Programmer considers NaNs. They now cannot use `==` until they
> rule out
>         // either a or b is NaN. This actually makes the code *more*
> complicated and
>         // less readable. Alternatively, they use `&==` or whatever we
> call the unsafe
>         // comparison and it’s just like what we had before, except now
> they have a
>         // “weird operator”.
>         if (!a.isNaN && !b.isNaN && a == b) || (a.isNaN && b.isNaN) {
>         }
>
> Now what happens if we return Bool?
>
>         // Programmer fails to consider NaN behavior.  Maybe the error
> when they
>         // wrote a == b clues them in that they should. Otherwise they
> just throw in
>         // a `!` and move on. They have the same bug they had before.
>         if (a == b)! {
>         }
>
>         // Programmer considers NaNs. Unchanged from what we have
> currently,
>         // except that we replace || with ??.
>         if a == b ?? (a.isNaN && b.isNaN) {
>         }
>
> If we are going to do the work of introducing another notion of
> floating-point equality, it should directly solve non-reflexivity of
> equality *by making equality reflexive*. My preferred approach would be to
> simply identify all NaNs:
>
>         // Programmer fails to consider NaN behavior. Now their code works!
>         if a == b {
>         }
>
>         // Programmer thinks about NaNs, realizes they can simplify their
> existing code:
>         if a == b {
>         }
>
> What are the downsides of this?
>
>         (a) it will confuse sometimes experts who expect IEEE 754
> semantics.
>         (b) any code that uses `a != a` as an idiom for detecting NaNs
> will be broken.
>
> (b) is by far the bigger risk. It *will* result in some bugs. Hopefully
> less than result from people failing to consider NaNs. The only real risk
> with (a) is that we get a biennial rant posted to hacker news about Swift
> equality being broken, and the response is basically “read the docs, use
> &== if you want that behavior”.
>

One of my premises for this discussion was that concrete NaN != NaN is
desirable, correct, and an absolute must-have; the question here was how to
write correct *generic* code given that Equatable currently guarantees a ==
a for all a. Do you disagree with the premise?


> One specific response:
>
> > I see the handling of NaN as a legacy/compatibility issue due to
> committee/vendor politics from the 1980’s.  I am pretty sure if they could
> do it over with modern tech, we would just have isNan() and NaN == NaN… or
> we might just have optionals instead.
>
> With the exception of how they interact with non-floating-point types
> (comparisons, conversions to/from integers and strings), NaNs are just
> Maybes with fast hardware support. Integers and booleans and strings are
> outside the scope of IEEE 754, so it was not in the standard’s purview to
> do anything else for those operations. They are not some exotic legacy
> thing leftover from the 1980’s; they were quite ahead of their time.
>
> – Steve
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-dev/attachments/20171031/8a246654/attachment.html>


More information about the swift-dev mailing list