我有以下代码:
func roughlyEq<T: FloatingPoint>(_ a: T, _ b: T) -> Bool {
return abs(a - b) < (0.01 as! T)
}
当我使用Float类型的参数调用它时,我在运行时遇到崩溃:
Could not cast value of type 'Swift.Double' (0x10043e6a8) to 'Swift.Float' (0x10043e5e0)
这没有任何意义 - 0.01可以确实转换为Float,比如当我写Float(0.01)
时。为什么会发生这种错误?
相反的方向也失败了:
Could not cast value of type 'Swift.Float' (0x1004ea5e0) to 'Swift.Double' (0x1004ea6a8).
如何让这种方法起作用?
答案 0 :(得分:2)
因为Double
和Float
是完全不相关的类型。即Double
不是类型/协议,是Float
的超类型,反之亦然。
请注意,仅仅因为存在要在Double
和Float
之间来回转换的初始值设定项,并不意味着您可以在它们之间来回转换。
FloatingPoint
协议可以很好地抽象浮点类型的行为,并完全根据Self
定义大部分内容,并且永远不会假设Double
或{ {1}}(或者任何具体类型)是一种比任何其他类型更特权或特殊的Float
类型。
我认为没有办法使用泛型函数扩展Self
,因为FloatingPoint
和Float
操作本质上是非泛型的(因为它们受到限制)到具体类型,以及实现它们的硬件)。
以下是我将如何做到这一点:
Double
关键是protocol DoubleOrFloat: FloatingPoint, ExpressibleByFloatLiteral {}
extension Float: DoubleOrFloat {}
extension Double: DoubleOrFloat {}
extension DoubleOrFloat {
func roughlyEquals(_ other: Self) -> Bool {
let closeEnoughThreshold: Self = 0.01
return abs(self - other) < closeEnoughThreshold
}
}
。这是可能的,因为let closeEnoughThreshold: Self = 0.01
是DoubleOrFloat
的子类型,因此我们知道浮点文字ExpressibleByFloatLiteral
可以转换为0.01
(因为已知Self
是Self
,暗示DoubleOrFloat
)。
答案 1 :(得分:2)
亚历山大已经说过,你不能从Double
强行施放
到Float
。文字常量0.01
的类型为Double
函数,如果as! T
为T
Float
将失败
一个简单的解决方案是为BinaryFloatingPoint
定义函数
相反,它描述了一个&#34;二进制浮点类型&#34;并扩展了FloatingPoint
协议。
所有常用的浮点类型(Float
,Double
和
CGFloat
)符合BinaryFloatingPoint
。
由于BinaryFloatingPoint
也继承自
ExpressibleByFloatLiteral
,您现在可以与之比较
0.01
字面意思:
func roughlyEq<T: BinaryFloatingPoint>(_ a: T, _ b: T) -> Bool {
return abs(a - b) < 0.01
}
此处0.01
的类型从上下文推断为T
。
或作为协议扩展方法:
extension BinaryFloatingPoint {
func roughlyEq(_ other: Self) -> Bool {
return abs(self - other) < 0.01
}
}