46

I tried to convert Decimal to Int with the follow code:

Int(pow(Decimal(size), 2) - 1) 

But I get:

.swift:254:43: Cannot invoke initializer for type 'Int' with an argument list of type '(Decimal)' 

Here I know pow is returning a Decimal but it seems that Int has no constructors and member functions to convert Decimal to Int.
How can I convert Decimal to Int in Swift 3?

CodeBender
  • 35,668
  • 12
  • 125
  • 132
Colin Wang
  • 6,778
  • 5
  • 26
  • 42

6 Answers6

43

This is my updated answer (thanks to Martin R and the OP for the remarks). The OP's problem was just casting the pow(x: Decimal,y: Int) -> Decimal function to an Int after subtracting 1 from the result. I have answered the question with the help of this SO post for NSDecimal and Apple's documentation on Decimal. You have to convert your result to an NSDecimalNumber, which can in turn be casted into an Int:

let size = Decimal(2)
let test = pow(size, 2) - 1
let result = NSDecimalNumber(decimal: test)
print(Int(result)) // testing the cast to Int
Community
  • 1
  • 1
tech4242
  • 2,348
  • 2
  • 23
  • 33
25
let decimalToInt = (yourDecimal as NSDecimalNumber).intValue

or as @MartinR suggested:

let decimalToInt = NSDecimalNumber(decimal: yourDecimal).intValue
Juan Boero
  • 6,281
  • 1
  • 44
  • 62
  • Worked great for me. Thanks! – Andres Paladines Dec 04 '20 at 18:27
  • 3
    This can fail sometimes... ```(lldb) po NSDecimalNumber(decimal: self) 10.6666666666666666666666666666666666666 (lldb) po NSDecimalNumber(decimal: self).intValue 0```. I think there is more of a discussion on it [here](https://stackoverflow.com/questions/56036637/nsdecimalnumberx-intvalue-returns-2-0-15-and-199-depending-on-the-amount-o), I think @Martin was pointing it out [here](https://bugs.swift.org/browse/SR-2980) – ScottyBlades Feb 02 '21 at 01:55
  • @ScottyBlades you are welcome to edit the answer with that fix – Juan Boero Apr 13 '21 at 18:47
  • 3
    This is answer is dangerously incorrect. Here's an example: `(lldb) po (Decimal(1201.68) as NSDecimalNumber).intValue -642` – marius Feb 24 '23 at 09:53
14

If you have a very long decimal, then beware of rounding errors

let decimal = Decimal(floatLiteral: 100.123456)
let intValue = (decimal as NSDecimalNumber).intValue // This is 100

However

let veryLargeDecimal = Decimal(floatLiteral: 100.123456789123)
let intValue = (veryLargeDecimal as NSDecimalNumber).intValue // This is -84 !

I ensured I rounded my Decimal before I converted it to an Int, using NSDecimalRound (which you can put in an extension of Decimal).

var veryLargeDecimal = Decimal(floatLiteral: 100.123456789123)
var rounded = Decimal()
NSDecimalRound(&rounded, &veryLargeDecimal, 0, .down)
let intValue = (rounded as NSDecimalNumber).intValue // This is now 100
bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
  • Absolutely right, thank you! Maybe `let veryLargeDecimal = Decimal(floatLiteral: 100.123456789123) let intValue = Int((veryLargeDecimal as NSDecimalNumber).doubleValue)` would be shorter? .doubleValue seems to work properly. – andreas1724 Apr 04 '20 at 23:04
  • 3
    There are people who go beyond apparent solutions and save lives :D – amar Nov 03 '20 at 10:01
  • Be careful!!! This would not work for negative values `-101`. Check the correct approach for getting the whole value of a decimal number as shown at this post [Getting the decimal part of a double in Swift](https://stackoverflow.com/a/55010456/2303865) – Leo Dabus Feb 21 '22 at 16:26
6

There is nothing wrong with either of the posted answers, but I would like to offer up an extension that reduces the verbosity for scenarios where you need to use this frequently.

extension Decimal {
    var int: Int {
        return NSDecimalNumber(decimal: self).intValue
    }
}

To call it:

let powerDecimal = pow(2, 2) // Output is Decimal
let powerInt = powerDecimal.int // Output is now an Int
CodeBender
  • 35,668
  • 12
  • 125
  • 132
2

Unfortunately there is an intermittent failure using some of the methods provided.

NSDecimalNumber(decimal: <num>).intValue can produce unexpected results...

(lldb) po NSDecimalNumber(decimal: self)
10.6666666666666666666666666666666666666

(lldb) po NSDecimalNumber(decimal: self).intValue
0

I think there is more of a discussion on it here, and @Martin was pointing it out here

Instead of using the decimal value directly, I made a work around that converts the decimal to a whole number before converting the Decimal to an Int.

extension Decimal {

   func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .down, scale: Int = 0) -> Self {
        var result = Self()
        var number = self
        NSDecimalRound(&result, &number, scale, roundingMode)
        return result
    }
    
    var whole: Self { rounded( self < 0 ? .up : .down) }
    
    var fraction: Self { self - whole }
    
    var int: Int {
        NSDecimalNumber(decimal: whole).intValue
    }
}
ScottyBlades
  • 12,189
  • 5
  • 77
  • 85
  • This looks familiar :) [Getting the decimal part of a double in Swift](https://stackoverflow.com/a/55010456/2303865) – Leo Dabus Feb 21 '22 at 16:18
  • I think the most concise approach would be using `plain` rounding as the default value. I have also updated the original post to use `sign == .minus` instead of `self < 0`. Note also that using `Self` instead of `Decimal` won't make any difference in this case other than syntax considering that `Decimal` is a struct not a protocol. – Leo Dabus Feb 21 '22 at 16:46
-3

Just use the description of Decimal, String replacement the NSDecimalNumber to bridge it.

extension Decimal {
    var intVal: Int? {
        return Int(self.description)
    }
}