3

I've got a really large decimal number in an NSString, which is too large to fit into any variable including NSDecimal. I was doing the math manually, but if I can't fit the number into a variable then I can't be dividing it. So what would be a good way to convert the string?

Example Input: 423723487924398723478243789243879243978234

Output: 4DD361F5A772159224CE9EB0C215D2915FA

I was looking at the first answer here, but it's in C# and I don't know it's objective C equivalent.

Does anyone have any ideas that don't involve using an external library?

Community
  • 1
  • 1
user688518
  • 461
  • 3
  • 15
  • 2
    The easiest thing to do would be finding a big integer library for iOS. Try compiling [MPIR](http://www.mpir.org/) for iOS. – Seva Alekseyev Mar 13 '13 at 20:22
  • As many of the commenters [on this question](http://stackoverflow.com/questions/1226949/biginteger-on-objective-c) mention, all you need to do is to find a BigInteger library for C, and then call it from your Objective-C code. – bdesham Mar 13 '13 at 20:25
  • possible duplicate of [Base 62 conversion in Objective-C](http://stackoverflow.com/questions/14780246/base-62-conversion-in-objective-c) – jscs Mar 13 '13 at 22:40

1 Answers1

1

If this is all you need, it's not too hard to implement, especially if you're willing to use Objective-C++. By using Objective-C++, you can use a vector to manage memory, which simplifies the code.

Here's the interface we'll implement:

// NSString+BigDecimalToHex.h
@interface NSString (BigDecimalToHex)
- (NSString *)hexStringFromDecimalString;
@end

To implement it, we'll represent an arbitrary-precision non-negative integer as a vector of base-65536 digits:

// NSString+BigDecimalToHex.mm
#import "NSString+BigDecimalToHex.h"
#import <vector>

// index 0 is the least significant digit
typedef std::vector<uint16_t> BigInt;

The "hard" part is to multiply a BigInt by 10 and add a single decimal digit to it. We can very easily implement this as long multiplication with a preloaded carry:

static void insertDecimalDigit(BigInt &b, uint16_t decimalDigit) {
    uint32_t carry = decimalDigit;
    for (size_t i = 0; i < b.size(); ++i) {
        uint32_t product = b[i] * (uint32_t)10 + carry;
        b[i] = (uint16_t)product;
        carry = product >> 16;
    }
    if (carry > 0) {
        b.push_back(carry);
    }
}

With that helper method, we're ready to implement the interface. First, we need to convert the decimal digit string to a BigInt by calling the helper method once for each decimal digit:

- (NSString *)hexStringFromDecimalString {
    NSUInteger length = self.length;
    unichar decimalCharacters[length];
    [self getCharacters:decimalCharacters range:NSMakeRange(0, length)];
    BigInt b;
    for (NSUInteger i = 0; i < length; ++i) {
        insertDecimalDigit(b, decimalCharacters[i] - '0');
    }

If the input string is empty, or all zeros, then b is empty. We need to check for that:

    if (b.size() == 0) {
        return @"0";
    }

Now we need to convert b to a hex digit string. The most significant digit of b is at the highest index. To avoid leading zeros, we'll handle that digit specially:

    NSMutableString *hexString = [NSMutableString stringWithFormat:@"%X", b.back()];

Then we convert each remaining base-65536 digit to four hex digits, in order from most significant to least significant:

    for (ssize_t i = b.size() - 2; i >= 0; --i) {
        [hexString appendFormat:@"%04X", b[i]];
    }

And then we're done:

    return hexString;
}

You can find my full test program (to run as a Mac command-line program) in this gist.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848