Why no signum() on floating point types?


(Jens Persson) #1

I tried but couldn't find any previous discussion about this, so:

BinaryInteger types have this method:

func signum() -> Self

(which returns -1 if this value is negative and 1 if it’s positive; otherwise, 0.)


Is there a reason why this method shouldn't be available on floating point types too?
(Note that it's not the same as FloatingPoints existing sign property.)


I guess things like -0 and +0, and nans with the sign bit set (or unset), infinity etc complicates it a bit, but what if it worked like the free function sign(x) that we get when importing simd:

sign(Double(0)) // 0
sign(-Double(0)) // 0
sign(Double.infinity) // 1
sign(-Double.infinity) // -1
sign(Double.nan) // 0
sign(-Double.nan) // 0

?


(Steve Canon) #2

I didn't add it when I wrote the FloatingPoint protocols because:

(a) I've never found it to be very useful.
(b) It's not an IEEE 754 required operation.
(c) It's easily confused with the sign property, which is a required operation and has distinct semantics.

I didn't consider this, but it's also trivially implementable as an extension, and there's not much optimization opportunity. So it seems like a tough sell to me, unless there's a really compelling use.

It exists for simd because it's common in shader languages and somewhat more useful for vector programming than for scalar.

Anyway, if you want to have it:

extension FloatingPoint {
  @inlinable
  func signum( ) -> Self {
    if self < 0 { return -1 }
    if self > 0 { return 1 }
    return 0
  }
}

or, probably worse code now, but more optimizable if we optimize some things in the stdlib and teach compiler how, something like:

extension FloatingPoint {
  @inlinable
  func signum( ) -> Self {
    return Self(signOf: self, magnitudeOf: self == 0 || self.isNaN ? 0 : 1)
  }
}

(Jens Persson) #3

FWIW, here's the piece of code where I noticed that it was missing:

    return self >= 0 ? x + abs(m) : x - abs(m)
    // Would perhaps be more efficient if written:
    // return x + m * self.signum() * m.signum()

(In an extension to BinaryFloatingPoint)


(Joe Groff) #4

x + copysign(m, self) should do the trick using standard IEEE-754 operations.


(Jens Persson) #5

or, as I just noticed:

return x + Self(signOf: self, magnitudeOf: m)

which will work in the context of BinaryFloatingPoint.
No idea if it's any more efficient than:

return self >= 0 ? x + abs(m) : x - abs(m)

though.


(Joe Groff) #6

copysign/init(signOf:magnitudeOf:) should be a bitwise operation, and you're avoiding a branch, so I would hope so!