也支持Decimal的通用计算函数



总之

当高精度很重要时,我正在玩计算来分析不同数字类型的可靠性和错误传播。同样的公式应用于不同的数字类型,我尝试使用泛型,但无法涵盖Decimal类型。

是否有一种方法,有一个单一的通用实现覆盖Float,DoubleDecimal的简单数值函数?

更多细节计算基于Muller序列,它递归地应用了一个非常简单的公式。这三种类型的代码是:

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)
}

泛型计算如下:

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)))

上面的代码没有编译,由于T没有初始化项的错误消息,这是有道理的。需要一些类型约束才能使其工作。所以我使用了T:Numeric,但是它在除法上失败了,错误是它不能应用于操作数T?T

几个错误之后,我结束了T:FloatingPoint。然后它适用于DoubleFloat,但我仍然不能与Decimal一起使用,因为Decimal不符合FloatingPoint。我没有找到任何其他适合我的类型约束的通用协议。

考虑到所有这三种数字类型都支持从整数初始化和四种基本操作,我无法想象没有办法编写一个泛型函数来涵盖它们。这就是我的问题。

方法1:定义一个BasicNumeric协议,包含您需要的函数

这里有一种方法。创建一个名为BasicNumericprotocol,它具有您想要使用的类型的共同属性。声明您的类型实现该协议,并在新函数中使用该协议作为约束:

protocol BasicNumeric: ExpressibleByIntegerLiteral {
static func +(_: Self, _: Self) -> Self
static func -(_: Self, _: Self) -> Self
static func *(_: Self, _: Self) -> Self
static func /(_: Self, _: Self) -> Self
}
extension Double:  BasicNumeric { }
extension Float:   BasicNumeric { }
extension Decimal: BasicNumeric { }
func mullerFormula<T: BasicNumeric>(y:T, z:T ) -> T {
let a = 1500/z
let b = (815 - a)/y
return 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)))

输出:

-7.306603773584911
-7.3066025
-7.30660377358490566037735849056603775

方法二:定义Divisible协议,使用Numeric协议

Leo Dabus在评论中提供了这个替代实现。

protocol Divisible {
static func /(_: Self, _: Self) -> Self
}
extension Double:  Divisible { }
extension Float:   Divisible { }
extension Decimal: Divisible { }
func mullerFormula<T: Numeric & Divisible>(y: T, z: T) -> T {
let a = 1500 / z
let b = (815 - a) / y
return 108 - b
}