deadbeef
deadbeef

Reputation: 5563

Failable Float to Int conversion in Swift

I'm trying to find a reliable way to convert a Float or a Double to an Int in Swift. I run into issues when there is an overflow.

Let's take the following example:

let double = 9223372036854775807.0 // This is 2^63 - 1 (aka Int.max on 64 bits architecture)

print("Int.max is         : \(Int.max)")
print(String(format: "double value is    : %f", double))
print(String(format: "Double(Int.max) is : %f", Double(Int.max)))

let int: Int
if (double >= Double(Int.max)) {
    print("Warning : double is too big for int !")
    int = Int.max
} else {
    int = Int(double)
}

Which prints:

 Int.max is         : 9223372036854775807
 double value is    : 9223372036854775808.000000
 Double(Int.max) is : 9223372036854775808.000000
 Warning : double is too big for int !

This approach is very cumbersome. Besides, you probably noticed that I test for double >= Double(Int.max) and not double > Double(Int.max). That's because Double(Int.max) is actually equal to Int.max + 1 because of rounding. And my code should probably have to be different for 32 bits architectures.

So is there another way? Like a failable initializer that I would have missed or a better, portable way to do this?

Upvotes: 1

Views: 297

Answers (1)

vacawama
vacawama

Reputation: 154641

You can extend Int by creating your own failable initializer for Double:

extension Int {
    init?(double: Double) {
        if double >= Double(Int.max) || double < Double(Int.min) || double.isNaN || double.isInfinite {
            return nil
        } else {
            self = Int(double)
        }
    }
}

if let i = Int(double: 17) {
    print(i)
} else {
    print("nil")
}

// It also handles NaN and Infinite
let nan = sqrt(-1.0)
let inf = 1e1000

Int(double: nan)   // nil
Int(double: inf)   // nil

Upvotes: 2

Related Questions