Ответ Мартина Р показывает правильный путь, но я все еще пишу это, чтобы научить тому, что происходит "под капотом".
Ограниченная точность 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