Float to Int conversion is documented to be a simple as:
let i = Int(x)
However this alone is unsafe, as a Swift app will crash if x is too big to fit in an integer, or is a NaN of some kind.
So what is the simplest, yet safe way to convert the unknown contents of a Float or Double to an Int (Int32, UInt16, etc.), e.g. without risking a crash? Is there an Int?() type? Or equivalent "if let" statement?
Since a float is bigger than int, you can convert a float to an int by simply down-casting it e.g. (int) 4.0f will give you integer 4. By the way, you must remember that typecasting just get rid of anything after the decimal point, they don't perform any rounding or flooring operation on the value.
To convert a float value to int we make use of the built-in int() function, this function trims the values after the decimal point and returns only the integer/whole number part. Example 1: Number of type float is converted to a result of type int.
The Float data type takes more bytes than the Int data type. So, we have to use explicit typecasting to convert the float value to an Int value.
Python also has a built-in function to convert floats to integers: int() . In this case, 390.8 will be converted to 390 .
Int(exactly:)
might be what you are looking for:
Creates an integer from the given floating-point value, if it can be represented exactly.
If the value passed as source is not representable exactly, the result is nil.
Example:
let x = 123e20
if let i = Int(exactly: x) {
print(i)
} else {
print("not representable")
}
This will also fail if the floating point number is not integral, so you might want to round it before the conversion:
let x = 12.3
if let i = Int(exactly: x.rounded(.towardZero)) {
print(i)
}
Rounding towards zero is what Int(x)
would do, you can pick your desired rounding mode.
Martin R's answer shows the right way, but I'm still writing this in order to teach what's going on "under the hood."
The limited precision of Double
means that only that at the magnitude of Int64.max
and Int64.min
, Double
representations are only available for every 4,096
Integers. As a result, there are a set of integers, which are valid and within range of Int64
, which after (lossy) conversion to Double, end up rounded to a magnitude no longer representable as Int64
. To account for these values, we need to ensure we only accept the range Double(Self.min).nextUp ... Double(Self.max).nextDown
, rather than 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
Here's what that looks like, in action
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)")
}
which prints:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With