64

I need to check if a double-defined variable is convertible to Int without losing its value. This doesn't work because they are of different types:

if self.value == Int(self.value)

where self.value is a double.

Youssef Moawad
  • 2,846
  • 5
  • 28
  • 50

12 Answers12

112

Try 'flooring' the double value then checking if it is unchanged:

let dbl = 2.0
let isInteger = floor(dbl) == dbl // true

Fails if it is not an integer

let dbl = 2.4
let isInteger = floor(dbl) == dbl // false
ColinE
  • 68,894
  • 15
  • 164
  • 232
  • 8
    This does not complete solve the question *"... if a double-defined variable is convertible to Int without losing its value."*. For example, it gives true for 3.1e100, but this value is not convertible to `Int` because it is too large. – Martin R Feb 11 '15 at 08:15
  • This is genius. – zeeshan Nov 14 '18 at 17:10
60

check if % 1 is zero:

Swift 3:

let dbl = 2.0
let isInteger = dbl.truncatingRemainder(dividingBy: 1) == 0

Swift 2:

let dbl = 2.0
let isInteger = dbl % 1 == 0
AamirR
  • 11,672
  • 4
  • 59
  • 73
rintaro
  • 51,423
  • 14
  • 131
  • 139
  • 4
    It would be nice if you could add some more informations, what the `% 1` does. I think that will help more people to understand the code. – Christian Feb 11 '15 at 08:04
  • 1
    he's using what's known as a modulo operator: https://en.wikipedia.org/wiki/Modulo_operation – newshorts Aug 07 '15 at 05:38
  • This will not work correctly. If you truncate 2.2 it is also true, but it's actually false because the value is 0.2. – adri567 Apr 08 '22 at 10:36
25

Swift 3

if dbl.truncatingRemainder(dividingBy: 1) == 0 {
  //it's an integer
}
William T.
  • 12,831
  • 4
  • 56
  • 53
18

There is now an Int(exactly:) initializer that will tell you this directly without the problem of out-of-range whole numbers.

if Int(exactly: self) != nil { ... }

This will only return a non-nil value if the result can actually be stored in Int exactly. There are many Double values that are "integers" but will not fit in an Int. (See MartinR's comment on the accepted answer.)

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
7

Simple Solution

I suggest converting the value to Int then to Double and checking the new value

if value == Double(Int(value)) {
// The value doesn't have decimal part. ex: 6.0

} else {
//  The value has decimal part. ex: 6.3

}
Musa almatri
  • 5,596
  • 2
  • 34
  • 33
6

A small extension to check for this:

extension FloatingPoint {
    var isInt: Bool {
        return floor(self) == self
    }
}

Then just do

let anInt = 1.isInt
let nonInt = 3.142.isInt
Ciprian Rarau
  • 3,040
  • 1
  • 30
  • 27
5

The Swifty way:

let cals = [2.5, 2.0]

let r = cals.map{ Int(exactly: $0) == nil ?  "\($0)" : "\(Int($0))" }

r // ["2.5", "2"]

Hide away in an extension:

extension Array where Element == Double {
    var orExactly: [String] {
        map{ Int(exactly: $0) == nil ?  "\($0)" : "\(Int($0))" }
    }
}

cals.orExactly
SmileBot
  • 19,393
  • 7
  • 65
  • 62
4

BECAREFUL.

truncatingRemainder(dividingBy:) can be tricky. See below:

Swift 4:

//case 1
let reminder = (4.1 * 100).truncatingRemainder(dividingBy: 1.0)

//case 2
let reminder2 = (410).truncatingRemainder(dividingBy: 1.0)

// reminder = 0.9999999999999432
// reminder2 = 0 
CocodyRockStar
  • 230
  • 2
  • 8
2
extension FloatingPoint {
    var isWholeNumber: Bool { isNormal ? self == rounded() : isZero }
}

let double = 3.0    
double.isWholeNumber        // true
print(3.15.isWholeNumber)   // false
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
2

Using mod (%) won't work anymore.

You can now use:

let dbl = 2.0
let isInteger = dbl.truncatingRemainder(dividingBy: 1.0) == 0.0
Chad
  • 382
  • 1
  • 11
2

How about converting the Double to an Int (which will cut off decimals), then back to a Double, then comparing this to the original Double? For example:

var dbl:Double = 22/3
dbl == Double(Int(dbl))
// false: dbl = 7.33333... Double(Int(dbl)) = 7.0

dbl = 25
dbl == Double(Int(dbl))
// true: dbl = 25.0, Double(Int(dbl)) = 25.0
Gallaugher
  • 1,593
  • 16
  • 27
2

Using @ColinE answer, I build an extension that handles when the Double cannot be converted to Int and another function that returns the Int:

extension Double {

    func isInt() -> Bool {
        guard Double(Int.min) <= self && self <= Double(Int.max) else {
            return false
        }

        return floor(self) == self
    }

    func toInt() -> Int? {
        guard Double(Int.min) <= self && self <= Double(Int.max) else {
            return nil
        }

        return Int(self)
    }
}

I hope this helps someone,

Xavi

XME
  • 515
  • 1
  • 6
  • 18