Nsnumber/Nsdecimalnumber Bizarre Behavior

{NSDecimalNumber integerValue} behaving strangely in iOS8

The result for integerValue is surprising, but from what I understand as documented:

NSDecimalNumber inherits from NSNumber. In the subclassing notes from NSNumber it is stated that
"... subclass must override the accessor method that corresponds to the declared type—for example, if your implementation of objCType returns “i”, you must override intValue ..."

objCType is set to the inner pointer, so it should be the same as for NSNumber.

NSDecimal does not override intergerValue.
It does override doubleValue, so that should work fine.

The only thing that makes me wonder: it seems it does not override intValue either ...

Converting NSString to NSNumber results in too many digits and strange rounding

This is just how float and double behaves in C/Objective-C (and many other languages). For example, when you type into python 8.0, the result would be 8.000000000001. I recommend using NSScanner to convert them into primitive number types (double, float).

cocoa - I've discovered what I think is a bug with NSDecimalNumber

Normally, I'm the one who comes in and explains to people that the number they entered is not representable as a floating-point number, and where the rounding errors are, blah blah blah.

This question is much more fun than what we usually see, and it illustrates exactly what's wrong with the crowd wisdom of "floating point is inexact, read 'what every computer scientist should know...' lolz".

36.76662445068359375 is not just any 19-digit decimal number. It happens to be a 19 digit decimal number that is also exactly representable in double precision binary floating point. Thus, the initial conversion implicit in:

double wtf = 36.76662445068359375000;

is exact. wtf contains exactly b100100.11000100010000011, and no rounding has occurred.

The spec for NSDecimalNumber says that it represents numbers as a 38 digit decimal mantissa and a decimal exponent in the range [-127,128], so the value in wtf is also exactly representable as an NSDecimalNumber. Thus, we may conclude that numberWithDouble is not delivering a correct conversion. Although I cannot find documentation that claims that this conversion routine is correctly rounded, there is no good reason for it not to be. This is a real bug, please report it.

I note that the string formatters on iPhoneOS seem to deliver correctly rounded results, so you can probably work around this by first formatting the double as a string with 38 digit precision and then using decimalNumberWithString. Not ideal, but it may work for you.

What is the right choice between NSDecimal, NSDecimalNumber, CFNumber?

If you are dealing with financial computations, you really should use base-10 arithmetic to avoid the rounding errors that can occur with the standard base-2 floating point types. So it's either NSDecimal or NSDecimalNumber. And since you're writing object-oriented code, NSDecimalNumber is the right choice for you.

To answer your questions: only testing of your code can reveal whether the memory overhead and performance loss are acceptable to you. I haven't really worked much with NSDecimalNumber but I'd wager that Apple's implementation is quite efficient and will be more than adequate for most people's needs.

Unfortunately, you won't be able to avoid the likes of decimalNumberByAdding: since Objective-C does not support operator overloading like C++ does. I agree that it makes your code somewhat less elegant.

One comment on the code you posted: r = [obj performSelector:NSSelectorFromString(@"capitalizedAmount")]; is rather unelegant. Either

r = [obj performSelector:@selector(capitalizedAmount)];

or even the simple

r = [obj capitalizedAmount];

would be better unless you require the NSSelectorFromString syntax for some other reason.

NSNumberFormatter rounding incorrectly when converting NSDecimalNumber to NSString

No, that looks like the correct behavior to me. If you look at the documentation of RoundHalfEven it says

Round towards the nearest integer, or towards an even number if equidistant.

In your case 3.785272 is closer to 3.79 than it is to 3.78, thus it is not equidistant and should not round towards an even number.

It should only round to an even number if it was truly equidistant, in your case 3.785 would round to 3.78.

NSDictionary returning same value for different keys

I think that when NSNumber and NSDecimalNumber are compared with each other, they are using doubleValue for comparison.

You can report bug in apple for that

I can suggest only to avoid NSNumber<->NSDecimalNumber comparison and use here:

NSString *value1 = dict[@(decimalKey1.longLongValue)];
NSString *value2 = dict[@(decimalKey2.longLongValue)];


Related Topics



Leave a reply



Submit