How can I count decimal digits? - ios

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.

Related

How to add more strings to the combined string if the length is less than prescribed length?

In iOS I am using two strings to combine and to form a single string. But the combined string must be of 16 characters . So if two strings are small and if we combine both and if it is less than 16 characters I must add few more characters to make it to 16 characters. How to achieve this?
NSString *combined = [NSString stringWithFormat:#"%#%#", stringURL, stringSearch];
This is the code I am using. So if I combine and it is less than 16 characters how to calculate it and add more characters to make it 16 characters?
Something like below
NSString *combined = [NSString stringWithFormat:#"%#%#", stringURL, stringSearch];
if (combined.length < 16)
{
NSString *newCombined = [NSString stringWithFormat:#"%#%#", combined, #"Some new string"];
}
You can use substringWithRange: method from NSString. You can take the below code as an example and modify it as per your requirements.
if (combined.length > 25)
{
NSString *beginning = [combined substringWithRange:NSMakeRange(0, 15)];
NSString *fromEnd = [combined substringWithRange:NSMakeRange(startPoint, combined.length-startPoint)];
}
You could make use of stringWithFormat - basically of printf if you want to pad with just a single char. Below I give some examples which I have constructed to illustrate, so it won't run out the box, but you only need to comment out the ones you do not want to make it work.
// To get 50 spaces
NSString * s50 = [NSString stringWithFormat:#"%*s", 50, ""];
// Pad with these characters, select only 1
// This will pad with spaces
char * pad = "";
// This will pad with minuses - you need enough to fill the whole field
char * pad = "-------------------------------------------------------";
// Some string
NSString * s = #"Hi there";
// Here back and front are just int's. They must be, but they can be calculated,
// e.g. you could have this to pad to 50
int back = 50 - s.length; if ( back < 0 ) back = 0;
// Pad s at the back
int back = 20;
NSString * sBack = [NSString stringWithFormat:#"%#%*s", s, back, pad];
// Pad s in front
int front = 10;
NSString * sFront = [NSString stringWithFormat:#"%*s%#", front, pad, s];
// Pad s both sides
NSString * sBoth = [NSString stringWithFormat:#"%*s%#%*s", front, pad, s, back, pad];
Note that the amounts here are parameterised. I use e.g. 50 in the first line but that could just as well be n as long as n is an int and you can use that to then perform calculations, store it in n and pad. There is an example in the code.
Here is a sample of the output
2020-11-04 08:16:22.908828+0200 FormatSpecifiers[768:15293] [Hi there-------------------------------------------------------]
2020-11-04 08:16:22.908931+0200 FormatSpecifiers[768:15293] [-------------------------------------------------------Hi there]
2020-11-04 08:16:22.908992+0200 FormatSpecifiers[768:15293] [-------------------------------------------------------Hi there-------------------------------------------------------]
I just show how to pad the combined string. To combine the string of course just use stringByAppendingString e.g.
NSString * s = [a stringByAppendingString:b];
and then you can do calcs based on s.length e.g. as shown in the example.

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

Get one digit after decimal point iOS Objective C

I am trying to get one digit after decimal and store it as double.
For eg : -
float A = 146.908295;
NSString * string = [NSString stringWithFormat:#"%0.01f",A]; // op 146.9
double B = [string doubleValue]; // op 146.900000
i want output as 146.9 in double or float form..,before duplicating or downvoting make sure the answer to this output is given..
Thanks
Edited:-
NSString * str = [NSString stringWithFormat:#"%0.01f",currentAngle];
tempCurrentAngle = [str doubleValue];;
tempCurrentAngle = tempCurrentAngle - 135.0;
if (tempCurrentAngle == 8.7) {
NSLog(#"DONE ");
}
here currentAngle is coming from continueTrackingWithTouch method, which will be in float..here it does not enter in if loop even when tempCurrentAngle value changes to 8.700000 .
You can compare string values instead of double like,
double currentAngle = 143.7; // I have taken static values for demo.
double tempCurrentAngle = 0.0;
NSString * str = [NSString stringWithFormat:#"%0.01f",currentAngle];
tempCurrentAngle = [str doubleValue];;
tempCurrentAngle = tempCurrentAngle - 135.0;
NSString *strToCompare = [NSString stringWithFormat:#"%0.01f",tempCurrentAngle];
if ([strToCompare isEqualToString:#"8.7"] ) {
NSLog(#"DONE ");
}
If you debug once line by line then you will get idea that why it was not entering in if caluse.
tempCurrentAngle get 143.69999999999999 when you convert str to double then you reduce 135.0 from it so it's value will be 8.6999999999999886 and then you compare it with 8.7 then it will definitely not being equal! but if you convert tempCurrentAngle string with one decimal point then it will be 8.7! so you should compare string values instead of double!

Truncate delimited NSString without removing delimiters

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:#":"];
}

Check to see if UITextField has a numeric value greater than 0

I have a UITextField and I only want a number greater than 0 ( I don't want non-numeric characters or the value 0 )
This is how I check to see if it is empty:
if(seizure.text.length==0)
This is how I check to see if it is equal to 0:
else if(seizure.text doubleValue]==0)
How can I check for non-numeric characters?
First check to see if you have any characters in the string, then check to make sure that it only contains numeric characters, and finally check to see if the value is greater than 0:
if (seizure.text.length > 0)
{
NSCharacterSet *nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
if ([seizure.text rangeOfCharacterFromSet:nonNumbers].location == NSNotFound)
{
if ([seizure.text doubleValue] > 0)
{
// Text Field contains a numeric value greater than 0
NSLog(#"Good number.");
return;
}
}
}
// If we make it to here, it does not meet your requirements.
NSLog(#"Bad Number.");
NSScanner will do the job nicely here. Unlike -[NSString doubleValue], its scanDouble: can parse and then also tell you whether it consumed the entire string, so you will know that there are non-numerical characters present.
Demonstration on some test cases. See the comments for descriptions of the expected results.
NSArray * texts = #[// First four unacceptable because non-numeric
#"", #"Hello, world!", #"1.0 excelsior", #"Jiminy 1.0 Crickets",
// These three unacceptable because 0 or less
#"0.0" #"0", #"-2048",
// Last three are good
#"3.14159", #"1", #"10000000000.0"];
for( NSString * text in texts ){
NSScanner * scanner = [NSScanner scannerWithString:text];
double val;
[scanner scanDouble:&val];
// Scanned the whole string and ended up with a positive value
if( [scanner isAtEnd] && val > 0 ){
NSLog(#"'%#'? I accept.", text);
}
else {
NSLog(#"'%#' is no good.", text);
}
}
[seizure.text doubleValue] == 0 will be true either if seizure.text is a textual representation of zero or if it doesn't contain a valid textual representation of a number (see the documentation for doubleValue).
In other words if this expression is false then you have a string value which starts with a number. However you still don't know if you string value contains only a number, e.g. [#"2.5 miles" doubleValue] has the value 2.5. If you need to handle strings like this you should look at NSScanner.

Resources