NSDecimal Problems

Hello All:


After the Xcode 10.1 (or perhaps ios 12.1), i noticed one of my tests were failing. Basically NSDecimalDivide is giving a bogus answer when the dividend is large and the divisior isn't. Here's a bit of code that shows the error:


int main(int argc, constchar * argv[])
{
    @autoreleasepool
    {
        NSDecimalNumber *xReg = [[NSDecimalNumber alloc] initWithString:@"200000000000000000000000000000000000000000000000000"];  // 2^50
        NSDecimalNumber *yReg = [[NSDecimalNumber alloc] initWithString:@"3.14159265358979323846264338327950288419"];
       
        NSDecimal xNum = [xReg decimalValue];
        NSDecimal yNum = [yReg decimalValue];       
       
        NSCalculationError calcError = NSDecimalDivide(&result, &xNum, &yNum, NSRoundPlain);
        NSLog(@"NSDecimalDivide %@. Calc Error is: %ld", NSDecimalString(&result, nil), (long)calcError);
       
        calcError = NSDecimalDivide(&result, &yNum, &xNum, NSRoundPlain);
        NSLog(@"NSDecimalDivide %@. Calc Error is: %ld", NSDecimalString(&result, nil), (long)calcError);
    }

    return 0;
}

Here's the output:


2018-11-11 12:55:47.113276-0500 NSDecimalDivideTest[37786:1866739] NSDecimalDivide 0. Calc Error is: 0
2018-11-11 12:55:47.113556-0500 NSDecimalDivideTest[37786:1866739] NSDecimalDivide 0.0000000000000000000000000000000000000000000000000157079632679489661923132169163975144209. Calc Error is: 0
Program ended with exit code: 0

The first divide is wrong. Last time I checked, the answer should be: 6.36619^49. The second divide is correct: 1.5707963^-50.


With something simple, setting xReg to 25 and yReg to 5, the results are correct: 5 and 0.2.


Is NSDecimalString having a problem with large values (doesn't seem to have a problem with small)? Has some behaviour changed? Any constructive comments would be appreciated.


Thanks in advance for your time...



John

Replies

Is this C++ or objC ?


When trying your code, I get several errors:


constchar does not exist

Use of undeclared identifier 'result'


Where are they both defined ?

I completed and tested.


Seems the max value is 2 10^33, which produces a strange result of

2018-11-12 16:40:23.957411+0100 Test ObjC[30108:2881969] NSDecimalDivide 666666666666666666666666666666666.66666. Calc Error is: 0

2018-11-12 16:40:23.957868+0100 Test ObjC[30108:2881969] NSDecimalDivide 0.00000000000000000000000000000000157079632679489661923132169163975144209. Calc Error is: 0



Normally, mantissa can be 38 digits:

NSDecimalNumber
, an immutable subclass of
NSNumber
, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as
mantissa x 10^exponent
where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.

I’m not sure what’s going on here but to make progress I need a better understanding of where you’re seeing differences. You wrote:

After the Xcode 10.1 (or perhaps ios 12.1) …

which is, alas, quite vague.

I took your code and ran it on a wide variety of Xcodes, simulators, and devices, and your first calculation always prints 0. Here’s the three most relevant examples:

  • Xcode 10.1 deploying on iOS 12.1

    2018-11-13 10:40:56.917845+0000 xxoi[1016:253833] NSDecimalDivide 0. Calc Error is: 0
    (lldb) p result 
    (NSDecimal) $1 = {
      _exponent = 50
      _length = 0
      _isNegative = 0
      _isCompact = 0
      _reserved = 0
      _mantissa = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0)
    }

    Here I set a breakpoint on line 14 of your code and printed

    result
    . As you can see, the
    NSLog
    has output 0 and all the elements of the
    _mantissa
    property are 0.
  • Xcode 10.1 deploying to iOS 11.4

    2018-11-13 10:39:52.209415+0000 xxoi[337:61257] NSDecimalDivide 0. Calc Error is: 0
    (lldb) p result 
    (NSDecimal) $0 = {
      _exponent = 50
      _length = 0
      _isNegative = 0
      _isCompact = 0
      _reserved = 0
      _mantissa = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0)
    }

    The results are the same as on 12.1.

  • Xcode 9.4 running on iOS 11.4

    2018-11-13 10:38:36.869943+0000 xxoi[334:60566] NSDecimalDivide 0. Calc Error is: 0
    (lldb) p result 
    (NSDecimal) $0 = {
      _exponent = 50
      _length = 0
      _isNegative = 0
      _isCompact = 0
      _reserved = 0
      _mantissa = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0)
    }

    And the results here are the same as they were with Xcode 10.1.

If you can give us a specific example of where you see a difference, I’d be happy to dig further into this.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Quinn. Sorry about being vague, I understand that completely. I should have provided more info.


But shouldn't the first result be 6.3661977^49? It's in the range of what an NSDecimal can handle and that's the answer my beatup-old HP-25C says.


John

Claude:


Sorry about the compile errors, I thought I had just copied the code out of Xcode and pasted it in here. I know about the range of an NSDecimal. Looks like a serious debugging session this weekend. 😉.


Thanks


John

I agree that 6.366…^49 is the correct result in a world of infinite precision (FYI, I checked the result with Python and it agrees with you HP-25C). I’m not sure that

NSDecimal
can do that, however. I’m just not enough of an expert in this field to offer a definitive answer on that front [1].

Which is why I was focusing on the change. If you were previously seeing that result and are now seeing 0, that’s something concrete to investigate.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] Although I will note the fabulous comment next to

NSDecimalDivide
in
<Foundation/NSDecimal.h>
:
// Division could be silently inexact;

)-:

Quinn:


Yeah, it's my test. In an ill-advised ;-) attempt at getting 'super accurate' results, I dragged out my old Quadra 840AV (amazed that it still booted and the good old days of Mac OS 8.1), installed my old copy of Mathematica and told it to give me some constants accurate to something silly - 100 decimal places. One of which was Pi. I used the Mathematica constant in my app and that's when I noticed the error. So if I use pi to 20 decimal places - 3.14159265358979323846 - all is good.


Thanks for your time and patience.



John

I tested with exponent


NSDecimalNumber *xReg2 = [NSDecimalNumber decimalNumberWithMantissa:2 exponent:50 isNegative:NO];


But, same wrong result as soon as exponent is more than 33. Not normal