PeonProgrammer
PeonProgrammer

Reputation: 1535

NSDecimalNumber issues

In my iOS swift application, I receive some json from the web which contains some double values which represent currency. It looks like this:

[{"Amount": 5.0},{"Amount":-26.07},{"Amount": 4}, ...etc]

I cast these as Doubles and then try to feed these values as a Swift "Double" into the NSDecimalNumber's constructor like this:

let amount = NSDecimalNumber(double: amountAsDouble)

I'm running into problems with this approach because very frequently the NSDecimalNumber I created will contain a different number that goes 16 places passed the decimal point.

let amount = NSDecimalNumber(double: -15.97)
println(amount)

this returns -15.970000000000004096

I don't want this, I want -15.97. Thanks,

Upvotes: 0

Views: 1110

Answers (5)

Ranjan
Ranjan

Reputation: 95

     let amount = NSDecimalNumber.init(value: -15.97)

    let roundValue = amount.rounding(accordingToBehavior: NSDecimalNumberHandler(roundingMode: .bankers, scale: 2, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false))

    print(roundValue)

Upvotes: -1

Van Du Tran
Van Du Tran

Reputation: 6892

A Double is stored with 18 decimal digits, you can't do anything about that, it's how it works.

Read here: http://en.wikipedia.org/wiki/Double-precision_floating-point_format

However, at the time of displaying the value on the screen, you can use NSNumberFormatter like this:

let amountInDouble: Double = -15.970000000000004096

let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.roundingIncrement = 0.01
formatter.maximumFractionDigits = 2

let amountAsString = formatter.stringFromNumber(NSNumber(double: amountInDouble))

if let amountAsString = amountAsString {
    println(amountAsString) // -15.97
}

Upvotes: 1

gnasher729
gnasher729

Reputation: 52632

Here's a tip: If you use NSJSONSerializer, numbers with decimal points are actually turned into NSDecimalNumber for you. NSDecimalNumber is a subclass of NSNumber. So what you are doing: You've got a perfectly fine NSDecimalNumber, round the value to double, and try to turn the double back into an NSDecimalNumber. Just check that what you have is indeed an NSDecimalNumber, and do no conversion if it is.

Upvotes: 1

blwinters
blwinters

Reputation: 2191

I recently went through this for myself. I ended up using an NSNumberFormatter to get the proper decimal places.

let currFormatter = NSNumberFormatter()
    currFormatter.numberStyle = .DecimalStyle
    currFormatter.roundingIncrement = 0.01
    currFormatter.minimumFractionDigits = 2
    currFormatter.maximumFractionDigits = 2

let doubleAmount = currFormatter.numberFromString(amountAsDouble) as NSNumber!
let amount = doubleAmount as Double
println(amount)

Upvotes: 1

thelaws
thelaws

Reputation: 8001

This is because the intermediate double representation is causing problems.

You should take the values from your dictionary as NSString objects and use the + decimalNumberWithString: method to convert without losing precision. In swift:

let amount = NSDecimalNumber(string: amountAsString)

Upvotes: -1

Related Questions