Swift 3 : Decimal to Int
Asked Answered
O

6

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?

Octonary answered 27/9, 2016 at 17:51 Comment(3)
Use Decimal only for currency values. Except that use double or float.Hemimorphic
NSDecimalNumber(decimal: yourDecimal).intValue should work.Brasilin
Why did you say Decimal(size) to begin with?Staggs
E
45

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
Enjambment answered 27/9, 2016 at 17:59 Comment(2)
I just wanted to know how to convert Decimal to Int, unfortunately you misunderstood my goal.Octonary
There is a pow(_ x: Decimal, _ y: Int) -> Decimal function.Brasilin
C
27
let decimalToInt = (yourDecimal as NSDecimalNumber).intValue

or as @MartinR suggested:

let decimalToInt = NSDecimalNumber(decimal: yourDecimal).intValue
Cellarage answered 20/2, 2018 at 14:20 Comment(4)
Worked great for me. Thanks!Torpor
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, I think @Martin was pointing it out hereCaper
@Caper you are welcome to edit the answer with that fixCellarage
This is answer is dangerously incorrect. Here's an example: (lldb) po (Decimal(1201.68) as NSDecimalNumber).intValue -642Unilobed
K
16

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
Koenig answered 4/12, 2019 at 12:50 Comment(3)
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.Non
There are people who go beyond apparent solutions and save lives :DHexad
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 SwiftCarboy
H
7

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
Handclap answered 19/4, 2019 at 17:29 Comment(1)
I love cute little extensions, but... there's no way to do this without passing through NS-prefixed Foundation classes?Hollie
C
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
    }
}
Caper answered 2/2, 2021 at 2:2 Comment(2)
This looks familiar :) Getting the decimal part of a double in SwiftCarboy
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.Carboy
S
-3

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

extension Decimal {
    var intVal: Int? {
        return Int(self.description)
    }
}
Scudder answered 30/10, 2019 at 2:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.