Why is Swift .intValue is returning incorrect/weird values?

314 Views Asked by At

I'm trying to convert a Decimal number which represents a dollar amount. Then I cast it into a NSDecimalNumber to apply .intValue transformation.

However I am getting weird behaviors. First it will give me an imprecise value when multiplying the value by 100. And when applying .intValue I get a completely unexpected number. Would appreciate your help!

//Issue
let dollars: Decimal = 106.99 * 100 // 10698.999999999997952
let cast = dollars as NSDecimalNumber // 10699
let int = cast.intValue //-7747


//No Problems here
let dollars2: Decimal = 106.98 * 100 // 10698
let cast2 = dollars2 as NSDecimalNumber // 10698
let int2 = cast2.intValue //10698
2

There are 2 best solutions below

5
AudioBubble On

Decimal's FloatLiteralType is Double.

Trouble is, 106.99 can't be represented by Double. Just popping it into a Decimal is problematic:

Decimal(106.99) == 106.98999999999997952 // true
106.99 as Decimal * 100 // 10698.999999999997952

So, you've got to sanitize your Doubles.

extension Decimal {
  init(dollarsAndCents: Double) {
    self = Self( (dollarsAndCents * 100).rounded() ) / 100
  }
}
extension Decimal {
  var dollarsAndCents: (dollars: Int, cents: Int) {
    (self * 100 as NSDecimalNumber).intValue
    .quotientAndRemainder(dividingBy: 100) as (Int, Int)
  }
}
Decimal(dollarsAndCents: 106.99).dollarsAndCents // (dollars: 106, cents: 99)

This is fine in practice because Decimal is for money. Sanitize and you'll never run into bugs.

0
Swati On

You can also try doing this

let dollars = "\(106.99 * 100)"
let cast = NSDecimalNumber(string: dollars)
let int = cast.intValue