Reputation: 13996
I'd like to make the function below work both with Float and Double values:
func srgb2linear(_ S: Float) -> Float {
if S <= 0.04045 {
return S / 12.92
} else {
return pow((S + 0.055) / 1.055, 2.4)
}
}
The Swift 4 Documentation says that what I need is a FloatingPoint
generic, to represent both Float and Double classes, like:
func srgb2linear<T: FloatingPoint>(_ S: T) -> T
However when I try to do this, it doesn't compile with the following errors:
Error: binary operator '<=' cannot be applied to operands of type 'T' and 'Double'
Error: binary operator '/' cannot be applied to operands of type 'T' and 'Double'
Error: binary operator '+' cannot be applied to operands of type 'T' and 'Double'
How is it possible that for a generic representing floating point numbers such operators are not implemented? And if not like this, how can I write this function in Swift?
Upvotes: 0
Views: 1907
Reputation: 386058
One problem is that FloatingPoint
is not a subprotocol of ExpressibleByFloatLiteral
, so your floating-point literals cannot necessarily be converted to T
. You can solve this either by changing FloatingPoint
to BinaryFloatingPoint
(which is a subprotocol of ExpressibleByFloatLiteral
) or by adding ExpressibleByFloatLiteral
as a separate requirement.
Then you will run into the problem that there is no pow
function that is generic over FloatingPoint
, and no member of FloatingPoint
or BinaryFloatingPoint
that performs exponentiation. You can solve this by creating a new protocol and conforming the existing floating-point types to it:
protocol Exponentiatable {
func toPower(_ power: Self) -> Self
}
extension Float: Exponentiatable {
func toPower(_ power: Float) -> Float { return pow(self, power) }
}
extension Double: Exponentiatable {
func toPower(_ power: Double) -> Double { return pow(self, power) }
}
extension CGFloat: Exponentiatable {
func toPower(_ power: CGFloat) -> CGFloat { return pow(self, power) }
}
Note that there is also a Float80
type, but the standard library doesn't provide a pow
function for it.
Now we can write a working generic function:
func srgb2linear<T: FloatingPoint>(_ S: T) -> T
where T: ExpressibleByFloatLiteral, T: Exponentiatable
{
if S <= 0.04045 {
return S / 12.92
} else {
return ((S + 0.055) / 1.055).toPower(2.4)
}
}
Upvotes: 3
Reputation: 42129
You could define a second one with Double arguments:
func srgb2linear(_ S: Double) -> Double {
if S <= 0.04045 {
return S / 12.92
} else {
return pow((S + 0.055) / 1.055, 2.4)
}
}
or, if Float<->Double conversions are not a concern :
func srgb2linear(_ S: Double) -> Double {
return Double(srgb2linear(Float(S)))
}
Upvotes: 0