总之
当高精度很重要时,我正在玩计算来分析不同数字类型的可靠性和错误传播。同样的公式应用于不同的数字类型,我尝试使用泛型,但无法涵盖Decimal
类型。
是否有一种方法,有一个单一的通用实现覆盖Float
,Double
和Decimal
的简单数值函数?
更多细节计算基于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
。然后它适用于Double
和Float
,但我仍然不能与Decimal
一起使用,因为Decimal
不符合FloatingPoint
。我没有找到任何其他适合我的类型约束的通用协议。
考虑到所有这三种数字类型都支持从整数初始化和四种基本操作,我无法想象没有办法编写一个泛型函数来涵盖它们。这就是我的问题。
方法1:定义一个BasicNumeric
协议,包含您需要的函数
这里有一种方法。创建一个名为BasicNumeric
的protocol
,它具有您想要使用的类型的共同属性。声明您的类型实现该协议,并在新函数中使用该协议作为约束:
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
}