浮点到整数转换的记录很简单:
let i = Int(x)
但是,仅此一项是不安全的,因为如果x太大而无法容纳整数,或者是某种NaN,Swift应用程序将崩溃。
那么将Float或Double的未知内容转换为Int(Int32,UInt16等)的最简单但安全的方法是什么,例如不会冒险崩溃?有Int?()类型吗?或等效的“如果让”语句?
答案 0 :(得分:3)
Int(exactly:)
可能就是您想要的:
从给定的浮点值创建一个整数(如果可以精确表示)。
如果作为源传递的值不能精确表示,则结果为nil。
示例:
let x = 123e20
if let i = Int(exactly: x) {
print(i)
} else {
print("not representable")
}
如果浮点数不是整数,这也会失败,因此您可能需要在转换前将其四舍五入:
let x = 12.3
if let i = Int(exactly: x.rounded(.towardZero)) {
print(i)
}
将Int(x)
舍入为零,您可以选择所需的舍入模式。
答案 1 :(得分:2)
Martin R的答案显示了正确的方法,但是我仍然在写这篇文章,以教“幕后”的情况。
Double
的有限精度意味着仅在Int64.max
和Int64.min
的量级上,Double
表示仅可用于每个4,096
整数。结果,存在一组有效且在Int64
范围内的整数,在(有损)转换为Double之后,这些整数最终舍入为不再可表示为Int64
的大小。为了说明这些值,我们需要确保仅接受范围Double(Self.min).nextUp ... Double(Self.max).nextDown
,而不接受Double(Self.min)... Double(Self.max)
Int.min -9,223,372,036,854,775,808
Float(Int.min) -9,223,372,036,854,780,000 lower than Int.min by 4096, thus not representable by Int
Float(Int.min).nextUp -9,223,371,487,098,960,000 greater than Int.min by 549,755,820,032, thus representable by Int
Int.max +9,223,372,036,854,775,807
Float(Int.max) +9,223,372,036,854,780,000 greater than Int.max by 4096, thus not representable by Int
Float(Int.max).nextDown +9,223,371,487,098,960,000 lower than Int.max by 549,755,820,032, thus representable by Int
实际情况就是这样
import Foundation
extension FixedWidthInteger {
static var representableDoubles: ClosedRange<Double> {
return Double(Self.min).nextUp ... Double(Self.max).nextDown
}
init?(safelyFromDouble d: Double) {
guard Self.representableDoubles.contains(d) else { return nil }
self.init(d)
}
}
func formatDecimal(_ d: Double) -> String{
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.positivePrefix = "+"
return numberFormatter.string(from: NSNumber(value: d))!
}
let testCases: [Double] = [
Double.nan,
-Double.nan,
Double.signalingNaN,
-Double.signalingNaN,
Double.infinity,
Double(Int.max),
Double(Int.max).nextDown,
+1,
+0.6,
+0.5,
+0.4,
+0,
-0,
-0.4,
-0.5,
-0.6,
-1,
-1.5,
Double(Int.min).nextUp,
Double(Int.min),
-Double.infinity,
]
for d in testCases {
print("Double: \(formatDecimal(d)), as Int: \(Int(safelyFromDouble: d)as Any)")
}
打印:
Double: NaN, as Int: nil
Double: NaN, as Int: nil
Double: NaN, as Int: nil
Double: NaN, as Int: nil
Double: +∞, as Int: nil
Double: +9,223,372,036,854,780,000, as Int: nil
Double: +9,223,372,036,854,770,000, as Int: Optional(9223372036854774784)
Double: +1, as Int: Optional(1)
Double: +0.6, as Int: Optional(0)
Double: +0.5, as Int: Optional(0)
Double: +0.4, as Int: Optional(0)
Double: +0, as Int: Optional(0)
Double: +0, as Int: Optional(0)
Double: -0.4, as Int: Optional(0)
Double: -0.5, as Int: Optional(0)
Double: -0.6, as Int: Optional(0)
Double: -1, as Int: Optional(-1)
Double: -1.5, as Int: Optional(-1)
Double: -9,223,372,036,854,770,000, as Int: Optional(-9223372036854774784)
Double: -9,223,372,036,854,780,000, as Int: nil
Double: -∞, as Int: nil
答案 2 :(得分:0)
我认为nil
是Float.nan
,Float.infinity
或超出范围Int.min...Int.max
的转换的适当返回值。
您可以定义一个范围,其中应包含浮点数:
let validRange: ClosedRange<Float> = Float(Int.min)...Float(Int.max)
并像这样使用它:
func convert(_ f: Float) -> Int? {
var optInteger: Int? = nil
if validRange.contains(f) {
optInteger = Int(f)
}
return optInteger
}
print(convert(1.2)) //Optional(1)
您应考虑到Float
和Int
之间的转换由于提供给每种类型的位数的限制而失去了大数值时的精度。例如,使用@MartinR的Int.init(exactly:)
不够精确:
let x: Float = 9223371487098961919.0
if let i = Int(exactly: x.rounded()) {
print(i)
}
收益
9223371487098961920
可能的解决方案是使用更精确的类型。例如Float80
:
let validRange: ClosedRange<Float80> = Float80(Int.min)...Float80(Int.max)
func convert(_ f80: Float80) -> Int? {
var optInteger: Int? = nil
if validRange.contains(f80) {
optInteger = Int(f80)
}
return optInteger
}
以下是一些用例:
convert(Float80(Int.max)) //Optional(9223372036854775807)
convert(Float80(Int.max) - 1.0) //Optional(9223372036854775806)
convert(Float80(Int.max) + 1.0) //nil
convert(Float80(Int.min))) //Optional(-9223372036854775808)
convert(Float80(Int.min) - 1.0)) //nil
convert(Float80.infinity) //nil
convert(Float80.nan) //nil