I'm trying to write generic half function for all numeric types:
func half<T where T: FloatingPointType>(value: T) -> T
{
return value * 0.5
}
And I see this error:
No '*' candidates produce the expected contextual result type 'T'
Is it possible to write generic function in this case?
Not in Swift 2.2. FloatingPointType
is not a very powerful type. In Swift 3 this is possible if you limit yourself to BinaryFloatingPoint
(which covers most of the floating point types you're probably thinking of, particularly Float
and Double
).
func half<T where T: BinaryFloatingPoint>(_ value: T) -> T {
return value * 0.5
}
In the most general case of a custom FloatingPointType
in Swift 2.2, the system may not have any way to convert this into a Double
multiplication and then convert that back into your arbitrary FloatingPointType
. The BinaryFloatingPoint
protocol adds ExpressibleByFloatLiteral
which means that 0.5
can be converted to your arbitrary type.
It's important to keep in mind that the 0.5
in this above code is of type T
. It is not a Double
. The following code still would not work:
func half<T where T: BinaryFloatingPoint>(_ value: T) -> T {
let oneHalf = 0.5
return value * oneHalf
// error: binary operator '*' cannot be applied to operands of type 'T' and 'Double'
}
You cannot multiply two arbitrary floating point numbers. Swift intentionally avoids a lot of automatic type coercion that was a source of subtle bugs in C. In the general case of a custom FloatingPoint
, it is not possible to say that one type is "more accurate" than the other. They may have different accuracy over different ranges. And of course you could create a FloatingPoint
that is much larger than a double. So there's no obvious "just promote everything to double" rule that you could use.
You write "all numeric types", but based on the 0.5
multiplication this answer is based on the assumption that you refer to the commonly use floating point types (Double
, Float
, CGFloat
).
You could create a custom protocol that blueprints the *
function (for multiplying to values of Self
) as well as an initializer by FloatLiteralType
, and conform Double
, Float
and CGFloat
to this protocol. The *
function as well as the initializer are already implemented for these three types, so the conformance is simply explicitly telling the compiler that these types indeed conform to your custom protocol.
E.g.
protocol MyFloatingPointTypes {
func *(lhs: Self, rhs: Self) -> Self
init(_: FloatLiteralType)
}
extension Double : MyFloatingPointTypes {}
extension Float : MyFloatingPointTypes {}
extension CGFloat : MyFloatingPointTypes {}
func half<T: MyFloatingPointTypes>(value: T) -> T {
return value * T(0.5)
}
/* example usage */
var dVal: Double = 10.5
var fVal: Float = 10.5
var cfVal: CGFloat = 10.5
dVal = half(dVal)
fVal = half(fVal)
cfVal = half(cfVal)
print(dVal, fVal, cfVal) // 5.25 5.25 5.25
FloatLiteralConvertible
As @Hamish writes in his comments below, rather than blueprinting the initializer by Double
(init(_: FloatLiteralType)
in MyFloatingPointTypes
, a better approach is to instead add an additional type constraint to the generic half(...)
function, constraining the generic (in addition to MyFloatingPointTypes
) to FloatLiteralConvertible
:
// lets rename this protocol here, since it no longer has any direct association with floats
protocol Multiplicable {
func *(lhs: Self, rhs: Self) -> Self
}
extension Double : Multiplicable {}
extension Float : Multiplicable {}
extension CGFloat : Multiplicable {}
func half<T: protocol<Multiplicable, FloatLiteralConvertible>>(value: T) -> T {
return value * 0.5
}
/* example usage */
// ... same as above
IntegerLiteralConvertible
Or, if you want you half
function to extend its generic "coverage" also to integer types (as also pointed out by @Hamish, thanks!) you could use the same compound protocol constraint method as above, but against IntegerLiteralConvertible
:
protocol Divisable {
func /(lhs: Self, rhs: Self) -> Self
}
extension Double : Divisable {}
extension Float : Divisable {}
extension CGFloat : Divisable {}
extension Int: Divisable {}
func half<T: protocol<Divisable, IntegerLiteralConvertible>>(value: T) -> T {
return value / 2
}
/* example usage */
var dVal: Double = 10.5
var fVal: Float = 10.5
var cfVal: CGFloat = 10.5
var iVal: Int = 11
dVal = half(dVal)
fVal = half(fVal)
cfVal = half(cfVal)
iVal = half(iVal) // truncates decimal part
print(dVal, fVal, cfVal, iVal) // 5.25 5.25 5.25 5
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With