I'm writing a program to compute the conversion of number system. My goal is to let the program compute very large number at a short time (<0.1s) and I made a NSString category to let me compute very large number (+ - * / %), a function to convert any base number to decimal, I think it's fast enough.
The problem is here: convert decimal to different bases. It is never fast enough, my algorithm even take me >10s to convert from decimal to binary or memory error.
- (NSString *)decimalToBase:(int)baseOfNewNumber
{
NSString *quotient, *remainder, *result;
result = #"";
quotient = number;
if (number != 0 && baseOfNewNumber != 10) {
for (int count = 0; [quotient compare:[NSString stringWithFormat:#"%i", baseOfNewNumber] options:NSNumericSearch] == (NSOrderedDescending|NSOrderedSame); count++) {
remainder = [quotient stringByModuloByString:[NSString stringWithFormat:#"%i", baseOfNewNumber]];
quotient = [quotient stringByDividedByString:[NSString stringWithFormat:#"%i", baseOfNewNumber]];
result = [remainder stringByAppendingString:result];
}
result = [[quotient stringByModuloByString:[NSString stringWithFormat:#"%i", baseOfNewNumber]] stringByAppendingString:result];
return result;
} else {
return number;
}
}
Lastly, this website converting the very very large number very fast, I saw its source code, it's likely simply using the usual method like me!
http://www.convertworld.com/en/numerals/
Related
I am making a game that requires me to use very large numbers. I believe I am able to store very large numbers with NSDecimal. However, when displaying the numbers to users I would like to be able to convert the large number to a succinct string that uses characters to signify the value eg. 100,000 -> 100k 1,000,000 -> 1.00M 4,200,000,000 -> 4.20B and so forth going up to extremely large numbers. Is there any built in method for doing so or would I have to use a bunch of
NSDecimalCompare statements to determine the size of the number and convert?
I am hoping to use objective c for the application.
I know that I can use NSString *string = NSDecimalString(&NSDecimal, _usLocale); to convert to a string could I then do some type of comparison on this string to get the result I'm looking for?
Use this method to convert your number into a smaller format just as you need:
-(NSString*) suffixNumber:(NSNumber*)number
{
if (!number)
return #"";
long long num = [number longLongValue];
int s = ( (num < 0) ? -1 : (num > 0) ? 1 : 0 );
NSString* sign = (s == -1 ? #"-" : #"" );
num = llabs(num);
if (num < 1000)
return [NSString stringWithFormat:#"%#%lld",sign,num];
int exp = (int) (log(num) / 3.f); //log(1000));
NSArray* units = #[#"K",#"M",#"G",#"T",#"P",#"E"];
return [NSString stringWithFormat:#"%#%.1f%#",sign, (num / pow(1000, exp)), [units objectAtIndex:(exp-1)]];
}
Some sample examples:
NSLog(#"%#",[self suffixNumber:#99999]); // 100.0K
NSLog(#"%#",[self suffixNumber:#5109999]); // 5.1M
Source
Solved my issue: Can only be used if you know that your NSDecimal that you are trying to format will only be a whole number without decimals so make sure you round when doing any math on the NSDecimals.
-(NSString *)returnFormattedString:(NSDecimal)nsDecimalToFormat{
NSMutableArray *formatArray = [NSMutableArray arrayWithObjects:#"%.2f",#"%.1f",#"%.0f",nil];
NSMutableArray *suffixes = [NSMutableArray arrayWithObjects:#"k",#"M",#"B",#"T",#"Qa",#"Qi",#"Sx",#"Sp",#"Oc",#"No",#"De",#"Ud",#"Dud",#"Tde",#"Qde",#"Qid",#"Sxd",#"Spd",#"Ocd",#"Nvd",#"Vi",#"Uvi",#"Dvi",#"Tvi", nil];
int dick = [suffixes count];
NSLog(#"count %i",dick);
NSString *string = NSDecimalString(&nsDecimalToFormat, _usLocale);
NSString *formatedString;
NSUInteger characterCount = [string length];
if (characterCount > 3) {
NSString *trimmedString=[string substringToIndex:3];
float a;
a = 100.00/(pow(10, (characterCount - 4)%3));
int remainder = (characterCount-4)%3;
int suffixIndex = (characterCount + 3 - 1)/3 - 2;
NSLog(#"%i",suffixIndex);
if(suffixIndex < [suffixes count]){
NSString *formatSpecifier = [formatArray[remainder] stringByAppendingString:suffixes[suffixIndex]];
formatedString= [NSString stringWithFormat:formatSpecifier, [trimmedString floatValue] / a];
}
else {
formatedString = #"too Big";
}
}
else{
formatedString = string;
}
return formatedString;
}
I am looking to write my own power function to work with NSDecimalNumbers and exponents that are not whole numbers. I first tried to use a combination of newtons method and the built in integer power method, but due to newtons method i am getting overflow errors when I have exponents with more than 2 decimals. So I thought maybe the float value pow function might serve as a good model for my own function. So I was wondering if anyone knows where I can fond some sort of documentation on the inner workings of the pow function?
Edit:
#wombat57, those links look like they could be what I am looking for however I have no idea to read them. The algorithm you suggest is in fact what I am using. the overflow comes from newtons method due to very large exponents. Because I am getting exponents in decimal form I have to convert it to a fraction first. the only way of ding this in code, as far as I know, multiplying the decimal by ten until you have a whole number, and using that as the numerator. Doing this you get exponents of 100+ for numbers with 3 or more decimals. this causes an overflow error.
EDIT 1: Here are links to the actual source
http://opensource.apple.com/source/Libm/Libm-2026/Source/Intel/expf_logf_powf.c
http://opensource.apple.com/source/Libm/Libm-315/Source/ARM/powf.c
I got the links from this question, which has a bunch of relevant discussion
self made pow() c++
This page describes an algorithm: Link.
x^(1/n) = the nth root of x, and x^mn = (x^m)^n. Thus, x^(m/n) = (the nth root of x)^m. Arbitrary roots can be calculated with Newton's method. Integer powers can be calculated with Exponentiation by squaring. For irrational exponents, you can use increasingly accurate rational approximations until you get the desired number of significant digits.
EDIT 2:
Newton's method involves raising your current guess to the power of the root that you're trying to find. If that power is large, and the guess is even a little too high, this can result in overflow. One solution here is to identify this case. If overflow ever occurs, this means that the guess was too high. You can solve the problem by (whenever a guess results in overflow), setting the current guess to a value between the last guess that did not overflow and the current guess (you may have to do this several times). That is, whenever Newton's method overflows, do a binary search down toward the last guess that did not overflow. Here's some python that implements all of this:
def nroot(n, b, sig_figs = 10):
g1 = 1.0
g2 = 1.0
while True:
done = False
while not done:
try:
g3 = g2 - ((g2**b) - n) / (b * (g2**(b-1)))
done = True
except OverflowError:
g2 = (g1 + g2) / 2.0
if abs(g2 - g3) < 1.0 / (10**sig_figs):
return g3
g1 = g2
g2 = g3
def npowbysqr(n, p):
if p == 0:
return 1.0
if p % 2 == 0:
v = npowbysqr(n, p/2)
return v*v
else:
return n*npowbysqr(n, p-1)
def npow(n, p):
return npowbysqr(nroot(n, 1000000), int(p*1000000))
print npow(5, 4.3467)
print 5**4.3467
I should add that there are probably much better solutions. This does seem to work, however
I happened to need something like this a while ago. Thankfully, Dave DeLong had been tinkering with this in his DDMathParser, so I built off of that. He yanked his implementation from his code in this commit, but I took that and modified it. This is my version of his NSDecimal power function:
extern NSDecimal DDDecimalPower(NSDecimal d, NSDecimal power) {
NSDecimal r = DDDecimalOne();
NSDecimal zero = DDDecimalZero();
NSComparisonResult compareToZero = NSDecimalCompare(&zero, &power);
if (compareToZero == NSOrderedSame) {
return r;
}
if (DDDecimalIsInteger(power))
{
if (compareToZero == NSOrderedAscending)
{
// we can only use the NSDecimal function for positive integers
NSUInteger p = DDUIntegerFromDecimal(power);
NSDecimalPower(&r, &d, p, NSRoundBankers);
}
else
{
// For negative integers, we can take the inverse of the positive root
NSUInteger p = DDUIntegerFromDecimal(power);
p = -p;
NSDecimalPower(&r, &d, p, NSRoundBankers);
r = DDDecimalInverse(r);
}
} else {
// Check whether this is the inverse of an integer
NSDecimal inversePower = DDDecimalInverse(power);
NSDecimalRound(&inversePower, &inversePower, 34, NSRoundBankers); // Round to 34 digits to deal with cases like 1/3
if (DDDecimalIsInteger(inversePower))
{
r = DDDecimalNthRoot(d, inversePower);
}
else
{
double base = DDDoubleFromDecimal(d);
double p = DDDoubleFromDecimal(power);
double result = pow(base, p);
r = DDDecimalFromDouble(result);
}
}
return r;
}
It tries to identify common cases and use more precise calculations for those. It does fall back on pow() for things that don't fit in these cases, though.
The rest of the NSDecimal functions I use can be found here and here.
I have come up with a function that suits my needs and will hopefully suit the needs of many others. the following method is fully annotated and works for any power function that has a real value. This method also only uses NSDecimalNumbers meaning you will not loose any precision due to float rounding error. This method takes two arguments one for the base and one for the power, and both are NSDecimalNumbers. So here it is:
//these are constants that will be used
NSDecimalNumber *ten = [NSDecimalNumber decimalNumberWithString:#"10"];
NSDecimalNumber *one = NSDecimalNumber.one;
//these will together hold the power in fractional form
NSDecimalNumber *numerator = power, *denominator = one;
//this will hold the final answer and all previous guesses the first guess is set to be the base
NSDecimalNumber *powAns = base;
//this will hold the change in your guess, also serves as an idea of how large the error is
NSDecimalNumber *error = one;
//part1 holds f(x) and part2 holds f'(x)
NSDecimalNumber *part1, *part2;
//if the base is < 0 and the power is not whole, answer is not real
if ([base doubleValue] < 0 && [[power stringValue] rangeOfString:#"."].location != NSNotFound)
return NSDecimalNumber.notANumber;
//converts power to a fractional value
while ([[numerator stringValue] rangeOfString:#"."].location != NSNotFound) {
numerator = [numerator decimalNumberByMultiplyingBy:ten];
denominator = [denominator decimalNumberByMultiplyingBy:ten];
}
//conditions here are the precision you wish to get
while ([error compare:[NSDecimalNumber decimalNumberWithString:#"1e-20"]] == NSOrderedDescending ||
[error compare:[NSDecimalNumber decimalNumberWithString:#"-1e-20"]] == NSOrderedAscending) {
//if this catches an overflow error it is set to be a very large number otherwise the value cannot be a number, however no other error should be returned.
#try {
part1 = [powAns decimalNumberByRaisingToPower:[denominator intValue]];
}
#catch (NSException *exception) {
if ([exception.name isEqual: NSDecimalNumberOverflowException])
part1 = [NSDecimalNumber decimalNumberWithString:#"10e127"];
else
return NSDecimalNumber.notANumber;
}
part1 = [part1 decimalNumberBySubtracting:base];
//if this catches an overflow error it is set to be a very large number otherwise the value cannot be a number, however no other error should be returned.
#try {
part2 = [powAns decimalNumberByRaisingToPower:[denominator intValue]-1];
part2 = [part2 decimalNumberByMultiplyingBy:denominator];
}
#catch (NSException *exception) {
if ([exception.name isEqual: NSDecimalNumberOverflowException])
part2 = [NSDecimalNumber decimalNumberWithString:#"10e127"];
else
return NSDecimalNumber.notANumber;
}
//error is the change in the estimated value or y - f(x)/f'(x)
error = [part1 decimalNumberByDividingBy:part2];
powAns = [powAns decimalNumberBySubtracting: error];
}
//if the numerator value is negative it must be made positive and the answer is then inverted
if ([numerator intValue] < 0) {
powAns = [powAns decimalNumberByRaisingToPower:abs([numerator intValue])];
powAns = [one decimalNumberByDividingBy:powAns];
}
else
powAns = [powAns decimalNumberByRaisingToPower:[numerator intValue]];
return powAns;
If anyone has any questions about my code I am happy to answer them.
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.
I am taking masses (as g, mg, µg, ng & kg) and volumes (as ml, µl & l) as input to a chemistry app.
Currently I convert all masses to grams and volumes to litres, save in core data and perform any calculations as doubles.
Finally results are then converted back into a meaningful unit
ie 0.000034litres is more useful expressed as 34µl for my customers
What is best practice for working between different units?
There may be a few libraries out there but what you are doing is specific so I doubt there is a specific "best practice".
You may want to investigate the properties of NSDouble, CGFloat however since they might be more suited across devices and give you more options them the primitive double.
There are no unit data types or many built in native converter functions. A number is a number its up to the programmer to give that number meaning in the app's context.
I found a suitable engineering formatter at https://github.com/dhoerl/EngineeringNotationFormatter
In addition I created a simple version:
-(NSString *)engineeringFormat:(double)value digits:(int)digits {
//calculate exponent in step of 3
int perMill = trunc(log10(value)/3);
if (value<1) {
perMill -= 1;
}
//calculate mantissa format range of 1 to 1000
double corrected = value;
while (corrected<1) {
corrected = corrected*1000;
}
while (corrected>=1000) {
corrected=corrected/1000;
}
//format number of significant digits
NSNumberFormatter *numberFormatDigits = [[NSNumberFormatter alloc] init];
numberFormatDigits.usesSignificantDigits = YES;
numberFormatDigits.maximumSignificantDigits = digits;
NSString *mantissa = [numberFormatDigits stringFromNumber:[NSNumber numberWithDouble:corrected]];
//select engineering notation prefix
NSArray *engSuffix = #[#"T",#"G",#"M",#"k",#"",#"m",#"µ",#"n",#"p"];
int index = 4 - perMill;
NSString *result;
if ((index > engSuffix.count-1) || (index<0)) {
result = #"Out of range";
} else {
result = [NSString stringWithFormat:#"%# %#",mantissa,[engSuffix objectAtIndex:index]];
}
return result;
}
I have some data in an NSString, separated by colons:
#"John:Doe:1970:Male:Dodge:Durango"
I need to limit the total length of this string to 100 characters. But I also need to ensure the correct number of colons are present.
What would be a reasonable to way to truncate the string but also add the extra colons so I can parse it into the correct number of fields on the other side?
For example, if my limit was 18, you would end up with something like this:
#"John:Doe:1970:Ma::"
Here's an updated version of my own latest pass at this. Uses #blinkenlights algorithm:
+ (NSUInteger)occurrencesOfSubstring:(NSString *)substring inString:(NSString *)string {
// http://stackoverflow.com/a/5310084/878969
return [string length] - [[string stringByReplacingOccurrencesOfString:substring withString:#""] length] / [substring length];
}
+ (NSString *)truncateString:(NSString *)string toLength:(NSUInteger)length butKeepDelmiter:(NSString *)delimiter {
if (string.length <= length)
return string;
NSAssert(delimiter.length == 1, #"Expected delimiter to be a string containing a single character");
int numDelimitersInOriginal = [[self class] occurrencesOfSubstring:delimiter inString:string];
NSMutableString *truncatedString = [[string substringToIndex:length] mutableCopy];
int numDelimitersInTruncated = [[self class] occurrencesOfSubstring:delimiter inString:truncatedString];
int numDelimitersToAdd = numDelimitersInOriginal - numDelimitersInTruncated;
int index = length - 1;
while (numDelimitersToAdd > 0) { // edge case not handled here
NSRange nextRange = NSMakeRange(index, 1);
index -= 1;
NSString *rangeSubstring = [truncatedString substringWithRange:nextRange];
if ([rangeSubstring isEqualToString:delimiter])
continue;
[truncatedString replaceCharactersInRange:nextRange withString:delimiter];
numDelimitersToAdd -= 1;
}
return truncatedString;
}
Note that I don't think this solution handles the edge case from CRD where the number of delimiters is less than the limit.
The reason I need the correct number of colons is the code on the server will split on colon and expect to get 5 strings back.
You can assume the components of the colon separated string do not themselves contain colons.
Your current algorithm will not produce the correct result when one or more of the characters among the last colonsToAdd is a colon.
You can use this approach instead:
Cut the string at 100 characters, and store the characters in an NSMutableString
Count the number of colons, and subtract that number from the number that you need
Starting at the back of the string, replace non-colon characters with colons until you have the right number of colons.
I tend towards #dasblinkenlight, it's just an algorithm after all, but here's some code. Few modern shorthands - used an old compiler. ARC assumed. Won't claim it's efficient, or beautiful, but it does work and handles edge cases (repeated colons, too many fields for limit):
- (NSString *)abbreviate:(NSString *)input limit:(NSUInteger)limit
{
NSMutableArray *fields = [[input componentsSeparatedByString:#":"] mutableCopy];
NSUInteger colonCount = fields.count - 1;
if (colonCount >= limit)
return [#"" stringByPaddingToLength:limit withString:#":" startingAtIndex:0];
NSUInteger nonColonsRemaining = limit - colonCount;
for (NSUInteger ix = 0; ix <= colonCount; ix++)
{
if (nonColonsRemaining > 0)
{
NSString *fieldValue = [fields objectAtIndex:ix];
NSUInteger fieldLength = fieldValue.length;
if (fieldLength <= nonColonsRemaining)
nonColonsRemaining -= fieldLength;
else
{
[fields replaceObjectAtIndex:ix withObject:[fieldValue substringToIndex:nonColonsRemaining]];
nonColonsRemaining = 0;
}
}
else
[fields replaceObjectAtIndex:ix withObject:#""];
}
return [fields componentsJoinedByString:#":"];
}