iOS - %G float can only handle six numbers - ios

Why does the %g format for strings only handle six numbers in a float and after that it turns into scientific notation? Is there any other way of displaying a float with something similar to the %g format but allows more than six numbers?
EDIT: I have figured out %g with precision i.e turning %g into %.Xg where x is the specified number of significant digits. But it doesnt help me in this situation:
-(IBAction)numberPressed:(id)sender {
if (decimalChecker == 1) {
currentDecimal = currentDecimal*10+ (float)[sender tag];
decimaledNumberString = [[NSString alloc] initWithFormat:#"%.17g.%.17g", currentNumber, currentDecimal];
calculatorScreen.text = decimaledNumberString;
currentDecimaledNumber = [decimaledNumberString floatValue];
NSLog(#"regular");
} else {
currentNumber = currentNumber*10+ (float)[sender tag];
calculatorScreen.text = [[NSString alloc] initWithFormat:#"%.17g", currentNumber];
NSLog(#"regular");
}
}
If I press "5" eight times instead of 55555555, I get 55551782 or something similar. How can I fix it to where I get the desired eight fives instead of the crazy number?

Insert a period and a numeral to specify the maximum number of significant digits you would like displayed, such as %.17g for 17 significant digits. As you discovered, the default is six.

According to http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Strings/Articles/FormatStrings.html#//apple_ref/doc/uid/20000943, iOS string formatting uses the same placeholders as C's printf(), which specifies g/G as representing FP values with exponential notation for very large/small values while f only uses non-exponential representation.
http://en.wikipedia.org/wiki/Printf_format_string#Format_placeholders

Related

Converting double to NSDecimalNumber while maintaining accuracy

I need to convert the results of calculations performed in a double, but I cannot use decimalNumberByMultiplyingBy or any other NSDecimalNumber function. I've tried to get an accurate result in the following ways:
double calc1 = 23.5 * 45.6 * 52.7; // <-- Correct answer is 56473.32
NSLog(#"calc1 = %.20f", calc1);
-> calc1 = 56473.32000000000698491931
NSDecimalNumber *calcDN = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:calc1];
NSLog(#"calcDN = %#", [calcDN stringValue]);
-> calcDN = 56473.32000000001024
NSDecimalNumber *testDN = [[[NSDecimalNumber decimalNumberWithString:#"23.5"] decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:#"45.6"]] decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:#"52.7"]];
NSLog(#"testDN = %#", [testDN stringValue]);
-> testDN = 56473.32
I understand that this difference is related to the respective accuracies.
But here's my question: How can I round this number in the most accurate way possible regardless of what the initial value of double may be? And if a more accurate method exists to do the initial calculation, what is that method?
Well, you can either use double to represent the numbers and embrace inaccuracies or use some different number representation, such as NSDecimalNumber. It all depends on what are the expected values and business requirements concerning accuracy.
If it is really crucial not to use arithmetic methods provided by NSDecimalNumber, than the rounding behaviour is best controlled using NSDecimalNumberHandler, which is a concrete implementation of NSDecimalNumberBehaviors protocol. The actual rounding is performed using decimalNumberByRoundingAccordingToBehavior: method.
Here comes the snippet - it's in Swift, but it should be readable:
let behavior = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundPlain,
scale: 2,
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false)
let calcDN : NSDecimalNumber = NSDecimalNumber(double: calc1)
.decimalNumberByRoundingAccordingToBehavior(behavior)
calcDN.stringValue // "56473.32"
I do not know of any method of improving the accuracy of the actual computations when using double representation.
I'd recommend rounding the number based on the number of digits in your double so that the NSDecimalNumber is truncated to only show the appropriate number of digits, thus eliminating the digits formed by potential error, ex:
// Get the number of decimal digits in the double
int digits = [self countDigits:calc1];
// Round based on the number of decimal digits in the double
NSDecimalNumberHandler *behavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundDown scale:digits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
NSDecimalNumber *calcDN = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:calc1];
calcDN = [calcDN decimalNumberByRoundingAccordingToBehavior:behavior];
I've adapted the countDigits: method from this answer:
- (int)countDigits:(double)num {
int rv = 0;
const double insignificantDigit = 18; // <-- since you want 18 significant digits
double intpart, fracpart;
fracpart = modf(num, &intpart); // <-- Breaks num into an integral and a fractional part.
// While the fractional part is greater than 0.0000001f,
// multiply it by 10 and count each iteration
while ((fabs(fracpart) > 0.0000001f) && (rv < insignificantDigit)) {
num *= 10;
fracpart = modf(num, &intpart);
rv++;
}
return rv;
}

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.

Check number of digits

This may be simple answer... What is the quickest algorithm to check if a number contains more than four digits? I am trying to create an if-then statement. If number x contains more than 4 digits, then...
So, I'm not sure if this is really as super basic as it sounds or not, but here's a couple of thoughts that might help:
Assuming it's an integer, can you simply test for it being 10,000 or greater (or -10,000 or less)?
if (abs(numberToTest) > 9999) {
// Number has 4 or more digits
} else {
// Number has 3 or fewer digits
}
Alternatively, if you're dealing with decimal values, you could try this:
NSString *stringToTest = [NSString stringWithFormat:#"%f", numberToTest];
// If you want to exclude the decimal place from the calculation
stringToTest = [stringToTest stringByReplacingOccurrencesOfString:#"."
withString:#""];
// If you want to include a negative sign from the calculation
stringToTest = [stringToTest stringByReplacingOccurrencesOfString:#"-"
withString:#""];
if (stringToTest.length > 4) {
// String has 5 or more digits
} else {
// String has 4 or fewer digits
}
Anyway, you didn't give a ton of detail, but maybe this solves your problem.
if its an int you could just subtract the number minus 9999 and determine if the result is negitive or positive but i assume its more complex than this
Yes you could just use an if statement
if (number > 9999)
print" we are in the 10000 range";
else
print " we are in less than 10000";
Is one line short enough?
let moreThanFourDigits = String(yourNumber).count > 4
Here I simply transform the number into a string and count the characters.

Is there an IOS framework for calculations with SI units

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;
}

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

Resources