> On Oct 21, 2017, at 6:27 PM, Xiaodi Wu via swift-dev <swift-dev@swift.org> 
> wrote:
> 
> Steve can describe the exact number of additional machine instructions on 
> each architecture, but from my cursory reading the performance cost is not 
> good and there's little cleverness to be had.

I'm not sure why you think there's no cleverness to be had. For instance, 
suppose we loosen our requirement a bit: We require the `==` operator to be 
reflexive, but we allow `BinaryFloatingPoint.==` to treat NaNs with different 
payloads as different values. That very nearly allows us to use integer 
equality for floating-point comparison; we only need a +0/-0 special case:

        extension Double {
                private var bits: Int {
                        return unsafeBitCast(self, to: Int64.self)
                }
                
                static func == (lhs: Double, rhs: Double) -> Bool {
                        let lhsBits = lhs.bits
                        let rhsBits = rhs.bits
                        let nonSignBits = ~((-0 as Double).bits)
                        
                        return lhsBits == rhsBits || ((lhsBits | rhsBits) & 
nonSignBits) == 0
                }
        }

`BinaryFloatingPoint.<` requires more branching, but it doesn't look too bad 
either:

        extension Double {
                static func < (lhs: Double, rhs: Double) -> Bool {
                        let lhsBits = lhs.bits
                        let rhsBits = rhs.bits
                        
                        let signBits = (-0 as Double).bits
                        let nonSignBits = ~signBits

                        if (lhsBits & signBits) == (rhsBits & signBits) {
                                // lhs < rhs (where signs are the same) is 
equivalent to integer comparison.
                                return lhsBits < rhsBits
                        }
                        else {
                                // lhs < rhs (where signs are different) if lhs 
is negative and they aren't both zero.
                                return (lhsBits & signBits) == 0 && ((lhsBits | 
rhsBits) & nonSignBits) != 0
                        }
                }
        }

(I could be wrong about these; I'm not a floating-point expert.)
                        
These implementations would make sorting and searching work *mostly* sensibly; 
the only problem would be that, for instance, `numbers.contains(.nan)` might 
return false if there's a NaN in `numbers` with a different payload than the 
one `.nan` returns. Personally, I think that's an acceptable cost.

> My overarching point is that, if most generic algorithms don't break without 
> a full equivalence relation and others can be trivially rewritten to 
> accommodate floating point behavior, then incurring a non-trivial cost by 
> default is not a good idea.

Yes, but the "if" is doing a lot of work here—and I don't think it's actually 
true. Things in the standard library that currently don't accommodate 
floating-point behavior include:

• Our `sort()`, `min()`, and `max()` methods
• Our `Set` and `Dictionary` types

Can these be trivially rewritten? I'm not sure about most of them, but I *can* 
come up with a trivial rewrite for `min()`:

  public func min() -> Element? {
    var it = makeIterator()
    guard var result = it.next() else { return nil }
    for e in IteratorSequence(it) {
      if !(result == result) || e < result { result = e }
    }
    return result
  }

But that brings us to the second question: Do we really expect random people to 
rewrite their code like this? I mean, we're literally doing a reflexivity check 
in this code. Is that a sensible thing to force on our users?

(For that matter, I'm not sure I could rewrite `min(by:)` this way if we treat 
the comparator as equivalent to a `<`. That seems like a sign we're Doing It 
Wrong.)

-- 
Brent Royal-Gordon
Architechies

_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Reply via email to