0

I'm trying to implement a generic sign function in Swift so that it can be used with any floating-point type:

func sign<T> (value:T) -> T {
    if value < 0.0 {
        return -1.0
    }
    if value > 0.0 {
        return 1.0
    }
    return 0.0
}

But this error stands in the way:

Binary operator '<' cannot be applied to operands of type 'T' and 'Double'

Desmond Hume
  • 8,037
  • 14
  • 65
  • 112

3 Answers3

2

If you constrain T to SignedNumberType, you'll be able to do everything you want. It inherits from Comparable and can convert from integer literals, the two things you need there.

func sign<T: SignedNumberType> (value:T) -> T {
    if value < 0 {
        return -1
    }
    if value > 0 {
        return 1
    }
    return 0
}
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • `SignedNumberType` does not include an initializer for `Double` arguments, so using this method alone; the `value < 0.0` comparison will be difficult to make generic. – dfrib Jan 24 '16 at 11:44
  • If you compare with 0 instead of 0.0 it'll work properly. Likewise the return values. – Nate Cook Jan 24 '16 at 11:45
  • Ah you are right, he only uses integer literals (as doubles) in his example above, my bad! – dfrib Jan 24 '16 at 11:47
  • My question was about floating-point literals in Swift generics. This answer is just good for implementing a sign function. – Desmond Hume Jan 24 '16 at 11:57
  • @DesmondHume ?? sign as is defined by Nate, works with Double and Float as well! returning type will be the same as type of parameter of the function. If you are looking for something else, do it ... and ask again. – user3441734 Jan 24 '16 at 12:19
1

You can create a type constraint (MyFloats below) to which your floating point types conform. You let this type constraint itself conform to Comparable, so that you may make use of the less than binary infix operator < when comparing the values of your generics. Also, for your example given above, the MyFloats type constraint need contain only a single blueprint; an initializer for a Double argument. This initalizer already exists for Double, Float and CGFloat types, but since a protocol cannot know which types that conforms to it, you need to include this blueprint.

protocol MyFloats : Comparable {
    init(_ value: Double)
}

extension Double : MyFloats { }
extension Float : MyFloats { }
extension CGFloat : MyFloats { }

func sign<T: MyFloats> (value:T) -> T {
    if value < T(0.0) {
        return T(-1.0)
    }
    if value > T(0.0) {
        return T(1.0)
    }
    return T(0.0)
}
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • @DesmondHume Happy to help. Note that since your `sign` function only contains "double literals" that are can in fact be represented as integer literals (`1.0` as `1`, `0.0` as `0` etc), I believe Nate Cooks solution below would be preferable for you, as you needn't create a custom protocol and extend types to this. Note however that the `SignedNumberType` solution will allow for all types that conform to this protocol (not just floating point types) to call your function `sign`. – dfrib Jan 24 '16 at 11:57
  • 1
    ... If you make use of Nates solution but only only want it available for floats (`Double`, `Float` and `CGFloat`), you could modify the type constraint in his solution below with the addition of conformance to protocol `FloatingPointType`: `func sign> ( ...` – dfrib Jan 24 '16 at 11:59
  • 1
    Your answer's interworkings told me more about Swift than others did and this solution is more flexible to be applied to other purposes. – Desmond Hume Jan 24 '16 at 12:00
  • Ok! I'll leave these comments here for thread visitors that possibly want less type constraint customization (-> Nates answer). – dfrib Jan 24 '16 at 12:03
0

Look at this answer. For your use case you need to create a new protocol that extends Comparable and extend the Float andDouble Swift types by using an extension.

Finally you'll be able to use this protocol as condition of the generic parameter.

func sign<T : MyCustomNumberProtocol> (value:T) -> T
Community
  • 1
  • 1
Patrick Leitermann
  • 2,144
  • 2
  • 13
  • 13
  • 1
    This will not solve the above as the protocol `NumericType` (in linked thread) does not explicitly conform to `Comparable`, hence existence of `<` operator cannot be inferred from the `MyCustomNumberProtocol` alone. – dfrib Jan 24 '16 at 11:38
  • 1
    @dfri thank's, that's true! I've forgotten this step in my description. Now I've updated my answer by this fact. – Patrick Leitermann Jan 24 '16 at 11:46