【问题标题】:Why do conversions of Float to Double fail at runtime?为什么 Float 到 Double 的转换在运行时会失败?
【发布时间】:2017-09-28 14:15:18
【问题描述】:

我有以下代码:

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(0.01) 时。为什么会出现这个错误?

相反的方向也失败了:

Could not cast value of type 'Swift.Float' (0x1004ea5e0) to 'Swift.Double' (0x1004ea6a8).

如何让这个方法起作用?

【问题讨论】:

    标签: swift casting floating-point type-conversion


    【解决方案1】:

    正如亚历山大已经说过的,你不能从Double强制转换 到Float。文字常量 0.01 在您的 函数,如果TFloat,则转换as! T 失败

    一个简单的解决方案是为BinaryFloatingPoint 定义函数 相反,它描述了“二进制浮点类型”并扩展了FloatingPoint 协议。 所有常用的浮点类型(FloatDoubleCGFloat) 符合 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
        }
    }
    

    【讨论】:

    • 这是否可以定义为成员函数,以便我可以做 a.roughlyEq(b),理想情况下适用于不同的浮点类型 a 和 b,或者至少适用于相同的类型?据推测,在前一种情况下,我们必须找到一种方法将两者转换为相同的类型,也许是通过 NSNumber。
    • @VaddadiKartick:查看更新的答案。如果您想比较不同的浮点类型,那么您可能必须定义一个自定义协议(如 Alexander 的回答),它允许将所有操作数转换为通用类型。
    • 有没有办法创建一个具有通用参数类型的 Double ?我不介意它是否因为反射或其他动态而变慢。
    • @VaddadiKartick:对不起,我不明白你的问题。
    • 我的意思是使用泛型类型 T 的参数调用 Double 的 init() 方法,而不是 Float 或 Double。也许使用反射。只要它在运行时有效,我不希望编译器妨碍。
    【解决方案2】:

    因为DoubleFloat 是完全不相关的类型。即Double 不是Float 的超类型的类/协议,反之亦然。

    请注意,仅仅因为存在初始化器可以在 DoubleFloat 之间来回转换,并不意味着您可以在它们之间来回转换。

    FloatingPoint 协议很好地抽象了浮点类型的行为,并且完全根据Self 定义了大多数事物,并且从不假设DoubleFloat(或任何具体的type) 是比其他任何类型都更有特权或特殊的 Self 类型。

    我认为没有办法使用通用函数扩展FloatingPoint,因为FloatDouble 操作本质上是非通用的(因为它们绑定到具体类型,并且实现它们的硬件)。

    我会这样做:

    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。有可能是因为DoubleOrFloatExpressibleByFloatLiteral的子类型,所以知道浮点字面量0.01可以转换为Self(已知SelfDoubleOrFloat,这意味着ExpressibleByFloatLiteral )。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-04
      • 2019-06-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多