2

Read about swift program exceptions in NSExceptionName,the explanation about decimalNumberExactnessException confused me.What exactly is decimal number exactness error and what kind code could get a decimalNumberExactnessException?

xf.l
  • 66
  • 3

1 Answers1

3

NSDecimalNumber has a precision of 38 decimal digits. That is large, but not unlimited. As an example, the sum of

let d1 = NSDecimalNumber(string: "12345678901234567890123456789")
let d2 = NSDecimalNumber(string: "0.000000000000000000123456789")

can not be represented as an NSDecimalNumber.

The default behavior is to ignore that precision loss and return the closest representable number:

let d1 = NSDecimalNumber(string: "12345678901234567890123456789")
let d2 = NSDecimalNumber(string: "0.000000000000000000123456789")

let d = d1.adding(d2)
print(d) // 12345678901234567890123456789

However, that default behavior can be changed, here is a simple example:

let handler = NSDecimalNumberHandler(roundingMode: .plain, scale: 0,
                               raiseOnExactness: true, raiseOnOverflow: true,
                               raiseOnUnderflow: true, raiseOnDivideByZero: true)
NSDecimalNumber.defaultBehavior = handler

let d1 = NSDecimalNumber(string: "12345678901234567890123456789")
let d2 = NSDecimalNumber(string: "0.000000000000000000123456789")

let d = d1.adding(d2)
// Terminating app due to uncaught exception 'NSDecimalNumberExactnessException',
// reason: 'NSDecimalNumber exactness exception'

The exception thrown in an NSException which can be caught in Objective-C with @try/@catch (but see Objective-C Exceptions for some caveats). An NSException can not be caught from within Swift.

In Swift you can use NSDecimalAdd instead if you need to check the result for possible precision loss:

var d1 = Decimal(string: "12345678901234567890123456789")!
var d2 = Decimal(string: "0.000000000000000000123456789")!
var r = Decimal()
let err = NSDecimalAdd(&r, &d1, &d2, .plain) // NSDecimalNumber.CalculationError
print(err == .lossOfPrecision) // true
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382