> On Oct 21, 2017, at 3:02 PM, Xiaodi Wu <[email protected]> wrote:
>
> On Fri, Oct 20, 2017 at 2:42 PM, Stephen Canon <[email protected]
> <mailto:[email protected]>> wrote:
>> On Oct 20, 2017, at 8:21 AM, David Zarzycki via swift-dev
>> <[email protected] <mailto:[email protected]>> wrote:
>>
>>> On Oct 20, 2017, at 07:51, Xiaodi Wu via swift-dev <[email protected]
>>> <mailto:[email protected]>> wrote:
>>>
>>> On Fri, Oct 20, 2017 at 1:22 AM, Jonathan Hull <[email protected]
>>> <mailto:[email protected]>> wrote:
>>> +1 for trapping unless using &==. In the case of ‘Float?’ we could also
>>> map to nil.
>>>
>>> This is probably a more appropriate discussion for evolution though...
>>>
>>>
>>>> On Oct 19, 2017, at 9:48 PM, Brent Royal-Gordon via swift-dev
>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>
>>>>> On Oct 19, 2017, at 4:29 PM, Xiaodi Wu via swift-dev <[email protected]
>>>>> <mailto:[email protected]>> wrote:
>>>>>
>>>>> D) Must floating-point IEEE-compliant equivalence be spelled `==`?
>>>>>
>>>>> 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.
>>>>
>>>> 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.
>>>>
>>>> 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.
>>>
>>>
>>> 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.
>>>
>>> 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.
>>
>> 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".
>>
>> In short: what IEEE requires of floating-point hardware is separable from
>> IEEE’s opinions about language/library design.
>
> 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.
>
> [The rest of this is a response to the thread as a whole, not to Dave]
>
> 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.
>
> 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.
>
> 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.
>
> Trapping only for == is much, much more palatable, but as Xiaodi said,
> doesn’t actually get you the semantics that you want for ==.
>
> &== 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.
>
> – Steve
>
> [*] 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.
>
> 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.
One counter argument is that &== will retain full speed where important (e.g.
in a tight loop).
Off the top of my head, a naive implementation of == for Float (where Nan ==
Nan) would be:
func == (a: Float, b: Float)->Bool {
return (a &== b) || !(a &== a) || !(b &== b)
}
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.
> 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.
>
> - `index(of:)` works perfectly sensibly without such a relation; if no NaN is
> equal to any other NaN, `index(of: .nan)` is appropriately `nil`.
> - `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.
> - `sort()` is problematic, but not if a custom predicate is supplied.
> - `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.
> - `==` is broken but can be fixed as shown in PR #12503.
>
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).
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.
Thanks,
Jon
_______________________________________________
swift-dev mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-dev