<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Oct 21, 2017, at 3:02 PM, Xiaodi Wu <<a href="mailto:xiaodi.wu@gmail.com" class="">xiaodi.wu@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class="">On Fri, Oct 20, 2017 at 2:42 PM, Stephen Canon <span dir="ltr" class=""><<a href="mailto:scanon@apple.com" target="_blank" class="">scanon@apple.com</a>></span> wrote:<br class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space" class=""><span class="gmail-"><blockquote type="cite" class="">On Oct 20, 2017, at 8:21 AM, David Zarzycki via swift-dev <<a href="mailto:swift-dev@swift.org" target="_blank" class="">swift-dev@swift.org</a>> wrote:<br class=""></blockquote><div class=""><blockquote type="cite" class=""><div class=""><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""><blockquote type="cite" class=""><div class="">On Oct 20, 2017, at 07:51, Xiaodi Wu via swift-dev <<a href="mailto:swift-dev@swift.org" target="_blank" class="">swift-dev@swift.org</a>> wrote:</div><br class="gmail-m_-6773026410006007789Apple-interchange-newline"><div class=""><div dir="ltr" class="">On Fri, Oct 20, 2017 at 1:22 AM, Jonathan Hull<span class="gmail-m_-6773026410006007789Apple-converted-space"> </span><span dir="ltr" class=""><<a href="mailto:jhull@gbis.com" target="_blank" class="">jhull@gbis.com</a>></span><span class="gmail-m_-6773026410006007789Apple-converted-space"> </span>wrote:<br class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word" class="">+1 for trapping unless using &==. In the case of ‘Float?’ we could also map to nil.<div class=""><br class=""></div><div class="">This is probably a more appropriate discussion for evolution though...<br class=""><div class=""><div class=""><br class=""></div><div class=""><br class=""><div class=""><div class=""><blockquote type="cite" class=""><div class=""><div class="gmail-m_-6773026410006007789h5"><div class="">On Oct 19, 2017, at 9:48 PM, Brent Royal-Gordon via swift-dev <<a href="mailto:swift-dev@swift.org" target="_blank" class="">swift-dev@swift.org</a>> wrote:</div><br class="gmail-m_-6773026410006007789m_2743765292807577601Apple-interchange-newline"></div></div><div class=""><div class=""><div class="gmail-m_-6773026410006007789h5"><div style="word-wrap:break-word;line-break:after-white-space" class=""><div class=""><blockquote type="cite" class=""><div class="">On Oct 19, 2017, at 4:29 PM, Xiaodi Wu via swift-dev <<a href="mailto:swift-dev@swift.org" target="_blank" class="">swift-dev@swift.org</a>> wrote:</div><br class="gmail-m_-6773026410006007789m_2743765292807577601Apple-interchange-newline"><div class=""><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">D) Must floating-point IEEE-compliant equivalence be spelled `==`?</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">In my view, this is something open for debate. I see no reason why it cannot be migrated to `&==` if it were felt that `==` *must* be a full equivalence relation. I believe this is controversial, however.</div></div></blockquote><br class=""></div><div class="">I actually got partway through writing up a pitch on this yesterday, but my opinion is that NaNs are so exceptional, and so prone to misuse, that we ought to treat them like integer arithmetic overflows: trap when they're detected, unless you use an `&` variant operator which indicates you know what you're doing.</div><div class=""><br class=""></div><div class="">I strongly suspect that, in practice, most float-manipulating code is not prepared to handle NaN and will not do anything sensible in its presence. For example, Apple platforms use floating-point types for geometry, color components, GPS locations, etc. Very little of this code will do anything sensible in the presence of a NaN. Arguably, it'd be better to exclude them through the type system, but I don't think that's a realistic possibility—we would need to have done that in a more source-break-friendly era. But that doesn't have to mean we're completely stuck.</div></div></div></div></div></blockquote></div></div></div></div></div></div></blockquote><div class=""><br class=""></div><div class="">Built-in floating point operators, as well as libc/libm math functions, are designed to propagate NaN correctly. This is not meant to be a thread about NaN, and we need to be cautious to define the scope of the problem to be solved from the outset. The tendency of having ever-expanding discussion where issues such as method names turn into discussions about the entire standard library go nowhere.</div><div class=""><br class=""></div><div class="">The question here is about `==` specifically and how to accommodate partial equivalence relations. For sanity, we start with the premise that NaN will forever be as it is.</div></div></div></div></div></blockquote><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">I support Jonathan’s argument. If Swift wants to trap on NaN to improve self-consistency and simplicity, then the tradeoff might be worth it. The alternative, teaching the Equality protocol about NaNs, feels like “the tail wagging the dog".</div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">In short: what IEEE requires of floating-point hardware is separable from IEEE’s opinions about language/library design.</div></div></blockquote><br class=""></div></span><div class="">Just to be precise: IEEE 754 places no requirements on hardware. The entirety of IEEE 754 is about what *languages* should provide. It just happens to be advantageous to implement many of the requirements directly in hardware.</div><div class=""><br class=""></div><div class="">[The rest of this is a response to the thread as a whole, not to Dave]</div><div class=""><br class=""></div><div class="">I have no philosophical objection to trapping on NaN. IEEE 754 says that the default behavior should be to not trap, but other non-default forms of exception handling* are explicitly allowed by IEEE 754.</div><div class=""><br class=""></div><div class="">From a practical standpoint, it’s is counter to everything about the way much floating-point hardware is designed, and that should give us some pause. On x86 it’s possible to unmask the “invalid floating point exception”, which results in any operation that generates a NaN faulting. However, it will *not* cause a fault if an operand is already a quiet NaN, so Swift would need to either test every operand that’s read from memory at the time that it’s moved into register, or test every result.</div><div class=""><br class=""></div><div class="">On some non-x86 architectures (including in particular most ARM implementations) there is no hardware support for unmasking exceptions, so there’s no way to automatically trap on invalid operations, you would have to explicitly check for NaN on every operation. This is much, much more expensive than checking for overflow on integer arithmetic (where for addition / subtraction, it’s just an easily-predicted conditional branch). Including these checks would introduce significant code bloat and slow down naive arithmetic by roughly an order of magnitude on current hardware, which is probably a non-starter.</div><div class=""><br class=""></div><div class="">Trapping only for == is much, much more palatable, but as Xiaodi said, doesn’t actually get you the semantics that you want for ==.</div><div class=""><br class=""></div><div class="">&== is ugly but workable. You will have inevitable bugs from people who naively adapt code from literally any other language that assumes IEEE 754 semantics for ==, however.</div><div class=""><br class=""></div><div class="">– Steve</div><div class=""><br class=""></div><div class="">[*] note that “exception handling” in an IEEE 754 context does not mean what you think it does if you’re coming from a generic CS not-floating-point background.</div></div></blockquote><div class=""><br class=""></div><div class="">The performance aspect of this issue are daunting, and I'm glad you brought it up. On a cursory reading, having every NaN value compare equal to every other NaN value is likely to be several times, if not orders of magnitude, slower than the hardware IEEE implementation. This would be an extraordinary cost and makes me wonder if this is at all a good idea. </div></div></div></div></div></blockquote><div><br class=""></div><div>One counter argument is that &== will retain full speed where important (e.g. in a tight loop).</div><div><br class=""></div><div>Off the top of my head, a naive implementation of == for Float (where Nan == Nan) would be:</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>func == (a: Float, b: Float)->Bool {</div><div><span class="Apple-tab-span" style="white-space:pre">                </span>return (a &== b) || !(a &== a) || !(b &== b) </div><div><span class="Apple-tab-span" style="white-space:pre">        </span>}</div><div><br class=""></div><div>The good news is that ‘a' and ‘b' only need to be loaded into registers once. Also, it could short circuit if 'a &== b' is true (if that is faster). We could probably do better with a little cleverness.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><div class="">It would serve us well to re-evaluate what generic algorithms absolutely require a full equivalence relation. Let's take a look at `Array`, for example.</div><div class=""><br class=""></div><div class="">- `index(of:)` works perfectly sensibly without such a relation; if no NaN is equal to any other NaN, `index(of: .nan)` is appropriately `nil`.</div><div class="">- `min()` and `max()` are something else entirely, as we're talking here only of equivalence and not of total order, which is another issue altogether.</div><div class="">- `sort()` is problematic, but not if a custom predicate is supplied.</div><div class="">- `split()` only runs into problems if specifically trying to split a sequence on `.nan`, but again this would be unsurprising if no NaN is equal to any other NaN.</div><div class="">- `==` is broken but can be fixed as shown in PR <span style="font-size:12.800000190734863px" class="">#12503. </span></div></div></div><div class="gmail_extra"><br class=""></div></div>
</div></blockquote></div><br class=""><div class="">My main worry would be generic algorithms on something one level of generalization away. For example, how should a Set handle double insertion of a struct which has a Float in it. It is very likely that the creator of that struct just && together == of the elements, so the set will end up with two identical entries (neither of which would register as being a member when the set is asked).</div><div class=""><br class=""></div><div class="">I am currently leaning towards NaN == NaN behavior, with a warning explaining &== when used with Float == Float (and a way to silence it). That way, you at least have progressive disclosure.</div><div class=""><br class=""></div><div class="">Thanks,</div><div class="">Jon</div><div class=""><br class=""></div><div class=""><br class=""></div></body></html>