How to exponentiate really large numbers in Objective-C? - ios

I'm having some trouble calculating the result of an 8-digit number to the power of a 3-digit number programmatically in Objective-C.
Take these numbers, for instance: 16468920^258, which should result in a number that is 1862 digits in length.
I naïvely tried:
unsigned long long result = 1;
for (int i = 0; i < 258; i++)
result *= 16468920;
…but result outputs 0.
Then I tried:
long double result = powl(16468920, 258);
…but result outputs inf.
After finding out about NSDecimal, I tried this:
NSDecimal result;
NSDecimal number = [[NSDecimalNumber decimalNumberWithString:#"16468920"] decimalValue];
NSDecimalPower(&result, &number, 258, NSRoundPlain);
…but result outputs NaN, so I tried:
NSDecimalNumber *number = [[NSDecimalNumber alloc] initWithInt:16468920];
NSDecimalNumber *result = [number decimalNumberByRaisingToPower:258];
…but this code raises an NSDecimalNumberOverflowException.
Any pointers as to which direction I should be going?

Since Objective-C is a superset of C, you can use a C library such a BN:
int BN_exp(BIGNUM *r, BIGNUM *a, BIGNUM *p, BN_CTX *ctx);
BN_exp() raises a to the p-th power and places the result in r ("r=a^p"). This
function is faster than repeated applications of BN_mul().
See, for example, here for how to get openssl into iOS.

You get that issue because your result still bigger that NSDecimalNumber could store.
I recommend you to could use JKBigInteger instead, it is a Objective-C wrapper around LibTomMath C library. And really easy to use and understand.
Hope this could help.

Related

How to convert a NSString to long double?

I am dealing with a long double value that can have huge values.
At one time I have this number represented as NSString and I need to convert it to long double. I see that the only API I have is
[myString doubleValue];
I don't see a longDoubleValue.
Trying to convert this number using doubleValue...
long double x = (long double)[#"3765765765E933" doubleValue];
gives me inf and the number in question is a legit long double value, as these numbers can go up to 1.18973149535723176502E+4932.
How do I do that?
Perhaps create a category on NSString yourself
NSArray *array = [myString componentsSeparatedByString:#"E"];
long double mantis = (long double)[array[0] doubleValue];
long double exponent = (long double)[array[1] doubleValue];
return mantis * exponent;
There will possibly be a loss of data though
edit
It would seem that long double on iOS is the same size as double. Maybe you will need a custom class to hold such large numbers.
You could probably do:
long double s = strtold(myString.UTF8String, NULL);
but if sizeof(long double) is the same as sizeof(double) as mag_zbc says, you might still get Inf.
If you want to go the pow() route, there is powl() which takes and returns long doubles.
You can do this using the C library sscanf function. Here is a sample Objective-C wrapper:
long double stringToLongDouble(NSString *str)
{
long double result = 0.0L;
int ret = sscanf(str.UTF8String, "%Lg", &result);
if (ret != 1)
{
// Insert your own error handling here, using NSLog for demo
NSLog(#"stringToLongDouble: could not parse '%#' as long double", str);
return 0.0L;
}
return result;
}
The return from sscanf will be 1 if it succeeds. For possible error returns see the documentation (man 3 scanf in Terminal) and you need to decide how to handle these, the above example just does an NSLog.
Note: The size & precision of long double may vary by platform/OS version. The above has been tested with your value on El Capitan and iOS 10 (simulator only) using Xcode 8.
HTH
In fact the answer of mag_zbc is almost there. The last line is incorrect.
Considering that the string has exponent, the correct is:
- (long double)longDoubleValue {
NSArray *array = [string componentsSeparatedByString:#"E"];
long double mantis = (long double)[array[0] doubleValue];
long double exponent = (long double)[array[1] doubleValue];
long double multiplier = powl(10.0L, exponent);
return mantis * multiplier;
}

Very big ID in JSON, how to obtain it without losing precision

I have IDs in JSON file and some of them are really big but they fit inside bounds of unsigned long long int.
"id":9223372036854775807,
How to get this large number from JSON using objectForKey:idKey of NSDictionary?
Can I use NSDecimalNumber? Some of this IDs fit into regular integer.
Tricky. Apple's JSON code converts integers above 10^18 to NSDecimalNumber, and smaller integers to plain NSNumber containing a 64 bit integer value. Now you might have hoped that unsignedLongLongValue would give you a 64 bit value, but it doesn't for NSDecimalNumber: The NSDecimalNumber first gets converted to double, and the result to unsigned long long, so you lose precision.
Here's something that you can add as an extension to NSNumber. It's a bit tricky, because if you get a value very close to 2^64, converting it to double might get rounded to 2^64, which cannot be converted to 64 bit. So we need to divide by 10 first to make sure the result isn't too big.
- (uint64_t)unsigned64bitValue
{
if ([self isKindOfClass:[NSDecimalNumber class]])
{
NSDecimalNumber* asDecimal = (NSDecimalNumber *) self;
uint64_t tmp = (uint64_t) (asDecimal.doubleValue / 10.0);
NSDecimalNumber* tmp1 = [[NSDecimalNumber alloc] initWithUnsignedLongLong:tmp];
NSDecimalNumber* tmp2 = [tmp1 decimalNumberByMultiplyingByPowerOf10: 1];
NSDecimalNumber* remainder = [asDecimal decimalNumberBySubtracting:tmp2];
return (tmp * 10) + remainder.unsignedLongLongValue;
}
else
{
return self.unsignedLongLongValue;
}
}
Or process the raw JSON string, look for '"id" = number; '. With often included white space, you can find the number, then over write it with the number quoted. You can put the data into a mutable data object and get a char pointer to it, to overwrite.
[entered using iPhone so a bit terse]

How can I count decimal digits?

I have to count how many decimal digits are there in a double in Xcode 5. I know that I must convert my double in a NSString, but can you explain me how could I exactly do? Thanks
A significant problem is that a double has a fractional part which has no defined length. If you know you want, say, 3 fractional digits, you could do:
[[NSString stringWithFormat:#"%1.3f", theDoubleNumber] length]
There are more elegant ways, using modulo arithmetic or logarithms, but how elegant do you want to be?
A good method could be to take your double value and, for each iteration, increment a counter, multiply your value by ten, and constantly check if the left decimal part is really near from zero.
This could be a solution (referring to a previous code made by Graham Perks):
int countDigits(double num) {
int rv = 0;
const double insignificantDigit = 8;
double intpart, fracpart;
fracpart = modf(num, &intpart);
while ((fabs(fracpart) > 0.000000001f) && (rv < insignificantDigit))
{
num *= 10;
fracpart = modf(num, &intpart);
rv++;
}
return rv;
}
You could wrap the double in an instance of NSNumber and get an NSString representation from the NSNumber instance. From there, calculating the number of digits after the decimal could be done.
One possible way would be to implement a method that takes a double as an argument and returns an integer that represents the number of decimal places -
- (NSUInteger)decimalPlacesForDouble:(double)number {
// wrap double value in an instance of NSNumber
NSNumber *num = [NSNumber numberWithDouble:number];
// next make it a string
NSString *resultString = [num stringValue];
NSLog(#"result string is %#",resultString);
// scan to find how many chars we're not interested in
NSScanner *theScanner = [NSScanner scannerWithString:resultString];
NSString *decimalPoint = #".";
NSString *unwanted = nil;
[theScanner scanUpToString:decimalPoint intoString:&unwanted];
NSLog(#"unwanted is %#", unwanted);
// the number of decimals will be string length - unwanted length - 1
NSUInteger numDecimalPlaces = (([resultString length] - [unwanted length]) > 0) ? [resultString length] - [unwanted length] - 1 : 0;
return numDecimalPlaces;
}
Test the method with some code like this -
// test by changing double value here...
double testDouble = 1876.9999999999;
NSLog(#"number of decimals is %lu", (unsigned long)[self decimalPlacesForDouble:testDouble]);
results -
result string is 1876.9999999999
unwanted is 1876
number of decimals is 10
Depending on the value of the double, NSNumber may do some 'rounding trickery' so this method may or may not suit your requirements. It should be tested first with an approximate range of values that your implementation expects to determine if this approach is appropriate.

iOS: calculating the remainder of a division for a long number

I have got an NSString * with for example the following numbers #"182316110006010135232100" and i need to do a calculation with this complete value. I have tried multiple types of number systems on iOS SDK for example Int, Float, etc. But because of the amount of bits it changes the number when i change the StringValue to for example an IntValue.
I need to do the following sum with this complete value: mod(digit, 97);
I have checked with for as far i know the longest type of number in Objective-C Long Long:
long long digit = [(NSString *)shouldBechecksum longLongValue];
And need to do the following calculation:
mod(digit, 97);
Now i get strange results because it does the sum with max version of the number. I need it to do this sum:
mod(182316110006010135232100, 97);
How can i do this calculation correctly?
Thanks!
You can use NSDecimalNumber class for precision up to 38 digits. To obtain the mod, just use this formula with the corresponding NSDecimalNumber methods you'll find explained in the documentation.
Mod = digit - int(digit/97)
This is because NSDecimalNumber can only do the basic operations, you have to obtain the mod as we did in school.
From Apple documentation:
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.
Fixed Thanks!
NSDecimalNumber *bigDecimal = [NSDecimalNumber decimalNumberWithString:shouldBechecksum];
NSDecimalNumber *divisor = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithDouble:97] decimalValue]];
NSDecimalNumber *quotient = [bigDecimal decimalNumberByDividingBy:divisor withBehavior:[NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundDown scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO]];
NSDecimalNumber *subtractAmount = [quotient decimalNumberByMultiplyingBy:divisor];
NSDecimalNumber *remainder = [bigDecimal decimalNumberBySubtracting:subtractAmount];
int checkSum = 98 - [remainder intValue];
I have done a little test with the following code snippet:
NSString *digitStr = #"182316110006010135232100";
long long digit = [(NSString *)digitStr longLongValue];
short checksum = digit % 97;
NSLog(#"%#, %lli, %lli, %i", digitStr, LONG_LONG_MAX, digit, checksum);
The result was:
182316110006010135232100, 9223372036854775807, 9223372036854775807, 78
This means that your value passes the LONG_LONG_MAX value. So, your problem is not feasible this way.
Remark: apparently Objective C puts the value closest to your number in the variabel digit, being LONG_LONG_MAX.
I guess you will have to find some kind of solution for even longer numbers to do what you want to do. Maybe NSDecimalNumber.
Kind regards,
PF

Odd atoi(char *) issue

I'm experiencing a very odd issue with atoi(char *). I'm trying to convert a char into it's numerical representation (I know that it is a number), which works perfectly fine 98.04% of the time, but it will give me a random value the other 1.96% of the time.
Here is the code I am using to test it:
int increment = 0, repetitions = 10000000;
for(int i = 0; i < repetitions; i++)
{
char randomNumber = (char)rand()%10 + 48;
int firstAtoi = atoi(&randomNumber);
int secondAtoi = atoi(&randomNumber);
if(firstAtoi != secondAtoi)NSLog(#"First: %d - Second: %d", firstAtoi, secondAtoi);
if(firstAtoi > 9 || firstAtoi < 0)
{
increment++;
NSLog(#"First Atoi: %d", firstAtoi);
}
}
NSLog(#"Ratio Percentage: %.2f", 100.0f * (float)increment/(float)repetitions);
I'm using the GNU99 C Language Dialect in XCode 4.6.1. The first if (for when the first number does not equal the second) never logs, so the two atoi's return the same result every time, however, the results are different every time. The "incorrect results" seemingly range from -1000 up to 10000. I haven't seen any above 9999 or any below -999.
Please let me know what I am doing wrong.
EDIT:
I have now changed the character design to:
char numberChar = (char)rand()%10 + 48;
char randomNumber[2];
randomNumber[0] = numberChar;
randomNumber[1] = 0;
However, I am using:
MAX(MIN((int)(myCharacter - '0'), 9), 0)
to get the integer value.
I really appreciate all of the answers!
atoi expects a string. You have not given it a string, you have given it a single char. A string is defined as some number of characters ended by the null character. You are invoking UB.
From the docs:
If str does not point to a valid C-string, or if the converted value would be out of the range of values representable by an int, it causes undefined behavior.
Want to "convert" a character to its integral representation? Don't overcomplicate things;
int x = some_char;
A char is an integer already, not a string. Don't think of a single char as text.
If I'm not mistaken, atoi expects a null-terminated string (see the documentation here).
You're passing in a single stack-based value, which does not have to be null-terminated. I'm extremely surprised it's even getting it right: it could be reading off hundreds of garbage numbers into eternity, if it never finds a null-terminator. If you just want to get the number of a single char (as in, the numeric value of the char's human-readable representation), why don't you just do int numeric = randomNumber - 48 ?

Resources