I'm quite puzzled by how complicated it is to make this simple function handle both Double and Float in Swift. Is it even possible?
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?
Now I tried with BinaryFloatingPoint, which by SE-0067 shouldn't even be needed, as BFP "adds some additional operations that only make sense for a fixed radix", but even BFP cannot handle the simple pow function.
Is it even possible to make this simplest function work on Double and Float in Swift?
You need to use BinaryFloatingPoint because FloatingPoint itself doesn't conform to ExpressibleByFloatLiteral. That's why you see these errors: Swift defaults to making your literal values of type Double.
pow isn't generic, but you can write your own extension to BinaryFloatingPoint that calls the right pow. It's not pretty, though. The Swift standard library doesn't actually offer floating-point math functions [yet]. You're relying on the C standard library here (although as an implementation detail pow specifically is actually intercepted by Swift and calls through to the LLVM intrinsic).
First problem: All of your float literals like 0.04045 are inferred to be Double. You need to stipulate that whatever type T is, it's a type that can be initialized from a float literal:
func srgb2linear<T: FloatingPoint & ExpressibleByFloatLiteral>(_ s: T) -> T {
if s <= 0.04045 {
return s / 12.92
} else {
return pow((s + 0.055) / 1.055, 2.4)
}
}
Now the literals are correctly being used to make instances of T.
Second issue: pow is not generic. It only exists as two concrete overloads, over types (Float, Float) -> Float and (Double, Double) -> Double. So I'll make a new protocol for Double and Float, and have it define a generic interface to access the respective overloads of pow:
The main issue here is that Swift has really horrible handling of basic math like pow, sin, etc. I would recommend NumericAnnex, which I suspect will eventually inform a future addition to the standard library.