In short
I was playing with calculations to analyse reliability and error propagation with different numeric type when high precision matters. The same formula being applied to different numeric types, I tried to use generics but was not able to cover also the Decimal type.
Is there a way to have a single generic implementation covering Float, Double and Decimal for simple numeric functions ?
More details
The calculation is based on the Muller sequence that applies a very simple formula recursively. The code for the three considered type is:
func mullerFormulaDouble(y:Double, z:Double ) -> Double {
return 108.0 - ( (815.0-1500.0/z)/y)
}
func mullerFormulaFloat(y:Float, z:Float ) -> Float {
return 108.0 - ( (815.0-1500.0/z)/y)
}
func mullerFormulaDecimal(y:Decimal, z:Decimal ) -> Decimal {
return Decimal(108) - ( (Decimal(815)-Decimal(1500)/z)/y)
}
The generic calculation looks like:
func mullerFormula<T>(y:T, z:T ) -> T { // a constraint is required for T
let a = T(1500)/z
let b = (T(815) - a)/y
return T(108) - b
}
print (mullerFormula(y:4.0, z:4.24))
print (mullerFormula(y:Float(4.0), z:Float(4.24)))
print (mullerFormula(y:Decimal(4), z:Decimal(sign:.plus, exponent: -2, significand:424)))
The code above did not compile, due to an error message about T having no initializer, which makes sense. Some type constraint is required to make it work. So I used T:Numeric, but it failed on the division, with the error that it cannot be applied to operands T? and T.
A couple of errors later, I ended up with T:FloatingPoint. It then worked for Double and Float, but I can still not use it with Decimal, because Decimal does not conform to FloatingPoint. I didn't find any other suitable common protocol for my type constraint.
Considering that all these three numeric types support initialization from an integer and the four basic operations, I cannot imagine that there is not way to write a generic function to cover them all. Hence my question.
Method 1: Define a
BasicNumericprotocol with the functions you requireHere is one way to do it. Create a
protocolcalledBasicNumericthat has the common properties of the types you want to use. Declare that your types implement that protocol, and use that protocol as the constraint in your new function:Output:
Method 2: Defining
Divisibleprotocol and usingNumericprotocolLeo Dabus offered this alternate implementation in the comments.