将NSNumber强制转换为浮点数时出现意外行为

4

在升级到Xcode 9.3(9E145)后,我的应用程序出现了一些意外行为。问题似乎在将NSNumber转换为Float时发生了。我使用as类型转换运算符来处理这个问题。请参考以下示例。

let n = NSNumber.init(value: 1.12)
let m = NSNumber.init(value: 1.00)

let x = n as? Float
let y = m as? Float

let xd = n as? Double

let z = Float(truncating: n)

在这里,第一个强制转换失败,即x == nil。第二个强制转换成功,并使用init:truncating构造函数实例化了一个浮点数,即z == 1.12。将n转换为Double成功,对我来说完全没有意义。

有人能向我解释这种行为吗?也就是说,有人能给我一个很好的理由,为什么将n强制转换为Float会失败吗?这是一个错误吗?如果这是预期的行为,您可以引用Swift文档中描述此行为的位置吗?


2
为什么要使用类型转换的 using as?为什么不使用 NSNumber 的 floatValue 和 doubleValue 方法呢? - rishi
@rishi:在我的应用程序中,我使用了as?运算符,因为要转换的值实际上是由Web服务(Firebase)返回的Any?Firebase使用NSNumber来表示数字值。 - Sander48k
1个回答

12
这是Swift 4中实现的SE-0170 NSNumber bridging and Numeric types的结果:

as?用于NSNumber,意味着“我能安全地将存储在这个名为NSNumber的不透明盒子中的值表示为我想要的值吗?”。

1.12是一个浮点数字面量,并被推断为Double,所以NSNumber(value: 1.12)将64位浮点值最接近1.12进行了"封箱"。将其转换为32位Float不能保留此值:
let n = NSNumber(value: 1.12)
let x = Float(truncating: n) // Or: let x = n.floatValue
let nn = NSNumber(value: x)
print(n == nn) // false

另一方面,{{1.0}}可以精确表示为{{Float}}类型:
let m = NSNumber(value: 1.0)
let y = m.floatValue
let mm = NSNumber(value: y)
print(m == mm) // true

这就是为什么将 m as? Float 进行转换成功的原因。两者

n.floatValue
Float(truncating: n)

可以用来“截断”数字到最接近可表示的32位浮点数值。


如何将 [NSArray] 转换为 [[Float]]?我是否需要先转换为 Double,然后在 for 循环中使用 Float 初始化器,还是有更简单的解决方案? - MuhsinFatih

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接