Losing double precision when dealing with timestamps - objective-c

I have a simple XCTestCase:
func testExample() {
let date = "2015-09-21T20:38:54.379912Z";// as NSString;
let date1 = 1442867934.379912;
XCTAssertEqual(date1, NSDate.sam_dateFromISO8601String(date).timeIntervalSince1970);
}
This test passes, when it shouldn't, for two reasons:
1442867934.379912 becomes 1442867934.379911 in the test, even though its literally just a variable re-printed
the sam_ function (copied below) seems to think the same way, the millisecond variable becomes millisecond double 0.37991200000000003 0.37991200000000003 which when later converted from NSDate to double, seems to lose the microsecond precision (debugger):
po NSDate.sam_dateFromISO8601String(date).timeIntervalSince1970 -> 1442867934.37991
po 1442867934.379912 -> 1442867934.37991
Any idea why? The microsecond precision (6 decimal places) is really important to my application, I need to seamlessly convert to and from NSString and NSDate using the iso8601 format.
+ (NSDate *)sam_dateFromISO8601String:(NSString *)iso8601 {
// Return nil if nil is given
if (!iso8601 || [iso8601 isEqual:[NSNull null]]) {
return nil;
}
// Parse number
if ([iso8601 isKindOfClass:[NSNumber class]]) {
return [NSDate dateWithTimeIntervalSince1970:[(NSNumber *)iso8601 doubleValue]];
}
// Parse string
else if ([iso8601 isKindOfClass:[NSString class]]) {
const char *str = [iso8601 cStringUsingEncoding:NSUTF8StringEncoding];
size_t len = strlen(str);
if (len == 0) {
return nil;
}
struct tm tm;
char newStr[25] = "";
BOOL hasTimezone = NO;
// 2014-03-30T09:13:00Z
if (len == 20 && str[len - 1] == 'Z') {
strncpy(newStr, str, len - 1);
}
// 2014-03-30T09:13:00-07:00
else if (len == 25 && str[22] == ':') {
strncpy(newStr, str, 19);
hasTimezone = YES;
}
// 2014-03-30T09:13:00.000Z
else if (len == 24 && str[len - 1] == 'Z') {
strncpy(newStr, str, 19);
}
// 2014-03-30T09:13:00.000000Z
else if (len == 27 && str[len - 1] == 'Z') {
strncpy(newStr, str, 19);
}
// 2014-03-30T09:13:00.000-07:00
else if (len == 29 && str[26] == ':') {
strncpy(newStr, str, 19);
hasTimezone = YES;
}
// Poorly formatted timezone
else {
strncpy(newStr, str, len > 24 ? 24 : len);
}
// Timezone
size_t l = strlen(newStr);
if (hasTimezone) {
strncpy(newStr + l, str + len - 6, 3);
strncpy(newStr + l + 3, str + len - 2, 2);
} else {
strncpy(newStr + l, "+0000", 5);
}
// Add null terminator
newStr[sizeof(newStr) - 1] = 0;
if (strptime(newStr, "%FT%T%z", &tm) == NULL) {
return nil;
}
double millisecond = 0.0f;
NSString *subStr = [[iso8601 componentsSeparatedByString:#"."].lastObject substringToIndex:6];
millisecond = subStr.doubleValue/1000000.f;
time_t t;
t = mktime(&tm);
return [NSDate dateWithTimeIntervalSince1970:t + millisecond];
}
NSAssert1(NO, #"Failed to parse date: %#", iso8601);
return nil;
}

1442867934.379912 and 1442867934.379911 may very well equal the same number just printed differently (one rounded, the other truncated)... if microsecond time is important to you, perhaps you should look at https://developer.apple.com/library/mac/qa/qa1398/_index.html
especially because [NSDate date] can change wildly when the clock changes... and an absolute time api will not...

I ended up simply changing my API to pass double representation of a Time object. i.e. 1442867934.379912not messing with ISO8601 at all.
NSDate supports this natively. So I don't have to parse any strings. Therefore, performance is an added bonus.
A couple quirks I encountered while doing this in Rails/Rabl/Oj:
Need to send the double as a string. It seems the JSONKit on iOS doesn't like more than 15 significant digits, and neither does the JSON spec.
Rabl (initializer):
Oj.default_options = {
use_to_json: true,
second_precision: 6,
float_precision: 16
}
The default to_f was not sending the precision I needed.

Related

`resolve_dtor() `Parentheses Algorithm

I'm running into a frustrating problem with a parentheses completion algorithm. The math library I'm using, DDMathParser, only processes trigonometric functions in radians. If one wishes to use degrees, they must call dtor(deg_value). The problem is that this adds an additional parenthesis that must be accounted for at the end.
For example, the math expression sin(cos(sin(x))) would translate to sin(dtor(cos(dtor(sin(dtor(x))) in my code. However, notice that I need two additional parentheses in order for it to be a complete math expression. Hence, the creation of resolve_dtor().
Here is my attempted solution, the idea was to have 0 signify a left-paren, 1 signify a left-paren with dtor(, and 2 signify a right-paren thus completing either 0 or 1.
- (NSMutableString *)resolve_dtor:(NSMutableString *)exp
{
NSInteger mutable_length = [exp length];
NSMutableArray *paren_complete = [[NSMutableArray alloc] init]; // NO/YES
for (NSInteger index = 0; index < mutable_length; index++) {
if ([exp characterAtIndex:index] == '(') {
// Check if it is "dtor()"
if (index > 5 && [[exp substringWithRange:NSMakeRange(index - 4, 4)] isEqual:#"dtor"]) {
//dtor_array_index = [self find_incomplete_paren:paren_complete];
[paren_complete addObject:#1];
}
else
[paren_complete addObject:#0]; // 0 signifies an incomplete parenthetical expression
}
else if ([exp characterAtIndex:index] == ')' && [paren_complete count] >= 1) {
// Check if "dtor("
if (![self elem_is_zero:paren_complete]) {
// Add right-paren for "dtor("
[paren_complete replaceObjectAtIndex:[self find_incomplete_dtor:paren_complete] withObject:#2];
[exp insertString:#")" atIndex:index + 1];
mutable_length++;
index++;
}
else
[paren_complete replaceObjectAtIndex:[self find_incomplete_paren:paren_complete] withObject:#2];
}
else if ([paren_complete count] >= 1 && [[paren_complete objectAtIndex:0] isEqualToValue:#2]) {
// We know that everything is complete
[paren_complete removeAllObjects];
}
}
return exp;
}
- (bool)check_dtor:(NSMutableString *)exp
{
NSMutableArray *paren_complete = [[NSMutableArray alloc] init]; // NO/YES
for (NSInteger index = 0; index < [exp length]; index++) {
if ([exp characterAtIndex:index] == '(') {
// Check if it is "dtor()"
if (index > 5 && [[exp substringWithRange:NSMakeRange(index - 4, 4)] isEqual:#"dtor"]) {
//dtor_array_index = [self find_incomplete_paren:paren_complete];
[paren_complete addObject:#1];
}
else
[paren_complete addObject:#0]; // 0 signifies an incomplete parenthetical expression
}
else if ([exp characterAtIndex:index] == ')' && [paren_complete count] >= 1) {
// Check if "dtor("
if (![self elem_is_zero:paren_complete]) {
// Indicate "dtor(" at index is now complete
[paren_complete replaceObjectAtIndex:[self find_incomplete_dtor:paren_complete] withObject:#2];
}
else
[paren_complete replaceObjectAtIndex:[self find_incomplete_paren:paren_complete] withObject:#2];
}
else if ([paren_complete count] >= 1 && [[paren_complete objectAtIndex:0] isEqualToValue:#2]) {
// We know that everything is complete
[paren_complete removeAllObjects];
}
}
// Now step back and see if all the "dtor(" expressions are complete
for (NSInteger index = 0; index < [paren_complete count]; index++) {
if ([[paren_complete objectAtIndex:index] isEqualToValue:#0] || [[paren_complete objectAtIndex:index] isEqualToValue:#1]) {
return NO;
}
}
return YES;
}
It seems the algorithm works for sin((3 + 3) + (6 - 3)) (translating to sin(dtor((3 + 3) x (6 - 3))) but not sin((3 + 3) + cos(3)) (translating to sin(dtor((3 + 3) + cos(dtor(3)).
Bottom Line
This semi-solution is most likely overcomplicated (one of my common problems, it seems), so I was wondering if there might be an easier way to do this?
Solution
Here is my solution to #j_random_hacker's pseudo code he provided:
- (NSMutableString *)resolve_dtor:(NSString *)exp
{
uint depth = 0;
NSMutableArray *stack = [[NSMutableArray alloc] init];
NSRegularExpression *regex_trig = [NSRegularExpression regularExpressionWithPattern:#"(sin|cos|tan|csc|sec|cot)" options:0 error:0];
NSRegularExpression *regex_trig2nd = [NSRegularExpression regularExpressionWithPattern:#"(asin|acos|atan|acsc|asec|acot)" options:0 error:0];
// need another regex for checking asin, etc. (because of differing index size)
NSMutableString *exp_copy = [NSMutableString stringWithString:exp];
for (NSInteger i = 0; i < [exp_copy length]; i++) {
// Check for it!
if ([exp_copy characterAtIndex:i] == '(') {
if (i >= 4) {
// check if i - 4
if ([regex_trig2nd numberOfMatchesInString:exp_copy options:0 range:NSMakeRange(i - 4, 4)] == 1) {
[stack addObject:#(depth)];
[exp_copy insertString:#"dtor(" atIndex:i + 1];
depth++;
}
}
else if (i >= 3) {
// check if i - 3
if ([regex_trig numberOfMatchesInString:exp_copy options:0 range:NSMakeRange(i - 3, 3)] == 1) {
[stack addObject:#(depth)];
[exp_copy insertString:#"dtor(" atIndex:i + 1];
depth++;
}
}
}
else if ([exp_copy characterAtIndex:i] == ')') {
depth--;
if ([stack count] > 0 && [[stack objectAtIndex:[stack count] - 1] isEqual: #(depth)]) {
[stack removeObjectAtIndex:[stack count] - 1];
[exp_copy insertString:#")" atIndex:i + 1];
}
}
}
return exp_copy;
}
It works! Let me know if there are any minor corrections that would be good to add or if there is a more efficient approach.
Haven't tried reading your code, but I would use a simple approach in which we scan forward through the input string writing out a second string as we go while maintaining a variable called depth that records the current nesting level of parentheses, as well as a stack that remembers the nesting levels that need an extra ) because we added a dtor( when we entered them:
Set depth to 0.
For each character c in the input string:
Write it to the output.
Is c a (? If so:
Was the preceding token sin, cos etc.? If so, push the current value of depth on a stack, and write out dtor(.
Increment depth.
Is c a )? If so:
Decrement depth.
Is the top of the stack equal to depth? If so, pop it and write out ).
DDMathParser natively supports using degrees for trigonometric functions and will insert the relevant dtor functions for you. It'll even automatically insert it by doing:
#"sin(42°)"
You can do this by setting the angleMeasurementMode on the relevant DDMathEvaluator object.

Scanning a file with getBytes:range:?

I'm currently scanning a binary file with the following code:
while (offset < headerLength) {
memset(buffer, 0, sizeof(buffer));
[bookData getBytes:buffer range:NSMakeRange(0 + offset, 4)];
NSString *output = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
if ([output isEqualToString:#"EXTH"]) {
...
}
offset++;
}
However, I suspect that's not the correct way, because it is quite slow. How to search for a specific string then?
GOOD BUT SLOW:
while (offset < headerLength) {
memset(buffer, 0, sizeof(buffer));
[bookData getBytes:buffer range:NSMakeRange(0 + offset, 4)];
if (buffer[0] == 'E' && buffer[1] == 'X' && buffer[2] == 'T' && buffer[3] == 'H') {
//run my stuff
break;
}
offset++;
}
memset(buffer, 0, sizeof(buffer));
Doesn't run my stuff
FILE * handle = fopen ([path UTF8String], "r");
while (1) {
fseek(handle, ++offset, SEEK_SET);
if (fgets(buffer, 4, handle)) {
if (buffer[0] == 'E' && buffer[1] == 'X' && buffer[2] == 'T' && buffer[3] == 'H') {
//doesn't run my stuff
break;
}
}
}
fclose(handle);
IO is slow, so I'd read in large chunks and avoid reading small chunks from a stream inside a loop. getBytes: makes a copy from the NSData object's bytes into your buffer, so you want to avoid doing that frequently too. Here is some untested pseudocode, assuming you have your NSData object to start off with:
const char *bytes = [bookData bytes];
while (offset < headerLengh - 4)
{
if (bytes[offset] == 'E' && bytes[offset + 1] == 'X' && bytes[offset + 2] == 'T' && bytes[offset + 3] == 'H')
{
//...
}
offset++;
}
I will note that there exist faster/more complicated algorithms such as boyer-moore for this type of search, but this should do for now.
I'd also advise against creating many NSString objects inside the loop. If you want to, you can create a NSString from your NSData object, and retrieve substrings inside the loop appropriately.
Instead of creating a full-fledged NSString on each iteration, you could just compare the raw bytes directly:
memset(buffer, 0, sizeof(buffer));
while (offset < headerLength) {
[bookData getBytes:buffer range:NSMakeRange(0 + offset, 4)];
if (buffer[0] == 'E' && buffer[1] == 'X' && buffer[2] == 'T' && buffer[3] == 'H') {
...
}
offset++;
}
This is assuming your buffer holds 4 chars, of course.
Also, I don't believe it's needed to clear the memory on each iteration with memset. Just once before the loop will do.
It's been a while since I practiced plain old C-style file reading, but basically, there it goes...
file handle = fopen('/var/mobile/...', 'r');
char *buf = malloc(4 * sizeof(char));
long offset = 0;
while (1) {
fseek(handle, ++offset);
ret = fgets(handle, 4, &buf); // returns NULL on EOF
if (ret) {
if (buf[0] == 'E' && ...) {
// Your logic goes here
break;
}
}
else
break;
}
fclose(handle);
Of course this is all pseudo-code, feel free to investigate or correct me.

Checking equality of arbitrary C types using Objective-C #encode -- already exists?

I'm looking for some code to do a general purpose equality comparison of arbitrary C types as supported by Objective-C's #encode() directive. I'm essentially looking for a function like:
BOOL CTypesEqual(void* a, const char* aEnc, void* b, const char* bEnc)
Which you might call like this:
struct { ... } foo = { yadda, yadda, yadda };
struct { ... } bar = { yadda, yadda, yadda };
BOOL isEqual = CTypesEqual(&foo, #encode(typeof(foo)), &bar, #encode(typeof(bar)));
Here's what I've discovered so far:
Revelation #1
You can't do this:
BOOL CTypesEqual(void* a, const char* aEnc, void* b, const char * bEnc)
{
if (0 != strcmp(aEnc, bEnc)) // different types
return NO;
NSUInteger size = 0, align = 0;
NSGetSizeAndAlignment(aEnc, &size, &align);
if (0 != memcmp(a, b, size))
return NO;
return YES;
}
...because of garbage in the spaces between members created by alignment restrictions. For instance, the following will fail the memcmp based equality check, despite the two structs being equal for my purposes:
typedef struct {
char first;
NSUInteger second;
} FooType;
FooType a, b;
memset(&a, 0x55555555, sizeof(FooType));
memset(&b, 0xAAAAAAAA, sizeof(FooType));
a.first = 'a';
a.second = ~0;
b.first = 'a';
b.second = ~0;
Revelation #2
You can abuse NSCoder to do this, like so:
BOOL CTypesEqual(void* a, const char* aEnc, void* b, const char * bEnc)
{
if (0 != strcmp(aEnc, bEnc)) // different types
return NO;
NSMutableData* aData = [[NSMutableData alloc] init];
NSArchiver* aArchiver = [[NSArchiver alloc] initForWritingWithMutableData: aData];
[aArchiver encodeValueOfObjCType: aEnc at: a];
NSMutableData* bData = [[NSMutableData alloc] init];
NSArchiver* bArchiver = [[NSArchiver alloc] initForWritingWithMutableData: bData];
[bArchiver encodeValueOfObjCType: bEnc at: b];
return [aData isEqual: bData];
}
That's great and all, and provides the expected results, but results in who knows how many heap allocations (at least 6) and makes an operation that should be relatively cheap, very expensive.
Revelation #3
You can't use NSValue for this. As in, the following does not work:
typedef struct {
char first;
NSUInteger second;
} FooType;
FooType a, b;
memset(&a, 0x55555555, sizeof(FooType));
memset(&b, 0xAAAAAAAA, sizeof(FooType));
a.first = 'a';
a.second = 0xFFFFFFFFFFFFFFFF;
b.first = 'a';
b.second = 0xFFFFFFFFFFFFFFFF;
NSValue* aVal = [NSValue valueWithBytes: &a objCType: #encode(typeof(a))];
NSValue* bVal = [NSValue valueWithBytes: &b objCType: #encode(typeof(b))];
BOOL isEqual = [aVal isEqual: bVal];
Revelation #4
Cocotron's NSCoder implementation punts on all the hard stuff (arbitrary structs, unions, etc.), thus is no source of further inspiration.
My attempt so far
So I started in on this, docs in hand, and I roughly got this far:
BOOL CTypesEqual(void* a, const char* aEnc, void* b, const char * bEnc)
{
if (0 != strcmp(aEnc, bEnc)) // different types
return NO;
return SameEncCTypesEqual(a, b, aEnc);
}
static BOOL SameEncCTypesEqual(void* a, void* b, const char* enc)
{
switch (enc[0])
{
case 'v':
{
// Not sure this can happen, but...
return YES;
}
case 'B':
case 'c':
case 'C':
case 's':
case 'S':
case 'i':
case 'I':
case 'l':
case 'L':
case 'q':
case 'Q':
case 'f':
case 'd':
case '#':
case '#':
{
NSUInteger size = 0, align = 0;
NSGetSizeAndAlignment(enc, &size, &align);
const int result = memcmp(a, b, size);
if (result)
return NO;
break;
}
case ':':
{
if (!sel_isEqual(*(SEL*)a, *(SEL*)b))
return NO;
}
case '*':
{
if (strcmp((const char *)a, (const char *)b))
return NO;
}
case '{':
{
// Get past the name
for (const char *prev = enc - 1, *orig = enc; prev < orig || (prev[0] != '=' && prev[0] != '\0' && enc[0] != '}'); prev++, enc++);
// Chew through it
for (NSUInteger pos = 0, size = 0, align = 0; enc[0] != '}' && enc[0] != '\0'; enc++, pos += size, size = 0, align = 0)
{
NSGetSizeAndAlignment(enc, &size, &align);
// figure out where we should be w/r/t alignment
pos = align * (pos + align - 1) / align;
// Descend
BOOL sub = SameEncCTypesEqual(((uint8_t*)a) + pos, ((uint8_t*)b) + pos, enc);
if (!sub)
return NO;
}
break;
}
case '[':
{
// Skip the '['
enc++;
// Get numElements
int numElements = 0;
sscanf(enc, "%d", &numElements);
// Advance past the number
for (; enc[0] <= '9' && enc[0] >= '0'; enc++);
// Get the size
NSUInteger size = 0, align = 0;
const char * const elementType = enc;
NSGetSizeAndAlignment(elementType, &size, &align);
for (NSUInteger i = 0; i < numElements; i++)
{
BOOL elementEqual = SameEncCTypesEqual(((uint8_t*)a) + i * size, ((uint8_t*)b) + i * size, elementType);
if (!elementEqual)
return NO;
}
break;
}
case '(':
{
NSLog(#"unions?! seriously, bro?");
return NO;
break;
}
default:
{
NSLog(#"Unknown type: %s", enc);
return NO;
break;
}
}
return YES;
}
...and about when I got to unions, I said to myself, "Self, why are you doing this? This is exactly the sort of code with a million little corner cases to miss, and really, it seems like something that should have been written a bunch of times already, by other people with way more patience." So here I am. Anyone know of a tried-and-true implementation of this in the (public) frameworks or "in the wild" that doesn't come with all the extra weight of using NSCoder?
Why not just make a function that skips the padding?
First, you would need to guess the padding policy. That's the easy part.
Then you use the encoding information provided by #encode to map the data types to masks, and use those masks to compare.
A trivial example:
struct m { char a; int b; };
struct n { char c; struct m d; int e; };
Could be transformed into (lets assume sizeof(int) is 4):
struct_m_mask = { 1, 4, 0 };
struct_n_mask = { 1, 1, 4, 4, 0 };
Optimization of the representation is of course possible in the case the alignment allows, e.g.:
struct_b_mask = { 2, 4, 4, 0 };
Then, you could walk this array to do the comparisons. A[n+1] - A[n] gives the hole size, if there's no hole ahead, like in the case of (b, e), then you can merge them.
That's the simplest way I could come up with. Probably you can implement more complex tricks.
BTW, my guess is that given the stuff is constant, the compiler could compute the masks at compile-time, but perhaps that's too much asking...
Regarding the padding issue you may have to make something like a function for each struct that turns it into an array for comparison or returns a single value at an index. It isn't very optimal but that is usually why sorting algorithms let you pass in function pointers that do the actual comparison.

Finding the numerator of a float's fractional value, given a specific denominator [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicates:
How to convert floats to human-readable fractions?
Convert decimal to fraction in Objective-C?
I want to take a decimal, 5.50, which is in a variable, and divide only the fractional part by 0.0625, which is my accuracy point. This would give me 8, as in 8/16 or 1/2.
Then I would like to display that answer as 5 8/16 or 1/2 in a text field. I know some answers will return a decimal still and not a whole number when dividing by .0625, but I would round that answer, which would still give me to the nearest 16th. What would be the best way to do this? I would like to make a function so I can reuse it. Yes, I posted a similar question that was answered, but wasn't able to get it to work. I'm thinking there's a better and easier way to do this, so I've posted this. Thanks in advance.
Function:
- (NSString *)theFunction:(float)input {
NSArray * array = [NSarray initWithObjects:nil,nil#"1/8",nil,#"1/4",]
int fractions = lroundf((input - (int)input)/((float)1/(float)16));
if(fractions == 0 || fractions == 16) {
return [NSString stringWithFormat:#"%d",lroundf(input)];
} else {
return [NSString stringWithFormat:#"%d %d/16",(int)input,fractions];
}
}
Note:
The if statement converts 5 0/16 into 5 and 5 16/16 into 6.
If you prefer the 5 0/16 and 5 16/16 notation, replace the if statement by:
return [NSString stringWithFormat:#"%d %d/16",(int)input,fractions];
EDIT: (by Jason)
//Just to make it a little sweeter!
- (NSString *)theFunction:(float)input {
int fractions = lroundf((input - (int)input)/((float)1/(float)16));
if(fractions == 0 || fractions == 16) {
return [NSString stringWithFormat:#"%d",lroundf(input)];
} else if(fractions == 2) {
return [NSString stringWithFormat:#"%d 1/8",(int)input];
} else if(fractions == 4) {
return [NSString stringWithFormat:#"%d 1/4",(int)input];
} else if(fractions == 6) {
return [NSString stringWithFormat:#"%d 3/8",(int)input];
} else if(fractions == 8) {
return [NSString stringWithFormat:#"%d 1/2",(int)input];
} else if(fractions == 10) {
return [NSString stringWithFormat:#"%d 5/8",(int)input];
} else if(fractions == 12) {
return [NSString stringWithFormat:#"%d 3/4",(int)input];
} else if(fractions == 14) {
return [NSString stringWithFormat:#"%d 7/8",(int)input];
} else {
return [NSString stringWithFormat:#"%d %d/16",(int)input,fractions];
}
}
EDIT (Response to edit by Jason)
I optimized your code, this way it's much cleaner.
Also check the code below, I think it's more efficient to use an array.
- (NSString *)theFunction:(float)input {
NSArray * array = [[NSArray alloc] initWithObjects: #"",#"",#"1/8",#"",#"1/4",#"",#"3/8",#"",#"1/2",#"",#"5/8",#"",#"3/4",#"",#"3/4",#"",#"7/8",#"",nil];
int fractions = lroundf((input - (int)input)/((float)1/(float)16));
if(fractions == 0 || fractions == 16) {
return [NSString stringWithFormat:#"%d",lroundf(input)];
} else {
if([[array objectAtIndex:fractions] isEqualToString:#""]) {
return [NSString stringWithFormat:#"%d %d/16",(int)input,fractions];
} else {
return [NSString stringWithFormat:#"%d %#",(int)input,[array objectAtIndex:fractions]];
}
}
}
I think your problem is much simpler than the one #Caleb linked to.
Divide by 0.0625 to get the number of sixteenths. Round the result to the nearest integer i. Use integer division i / 16 to get the number of whole units and use the modulo operator j = i % 16 to get the fractional units. To reduce the fractions, use j as an index into an array you create ahead of time as { "", "1/16", "1/8", "3/16", ... }.
You can also divide by 16 and get the modulo 16 using bitwise operators, but that will make the code harder to read for not much benefit.

Compare version numbers in Objective-C

I am writing an application that receives data with items and version numbers. The numbers are formatted like "1.0.1" or "1.2.5". How can I compare these version numbers? I think they have to be formatted as a string first, no? What options do I have to determine that "1.2.5" comes after "1.0.1"?
This is the simplest way to compare versions, keeping in mind that "1" < "1.0" < "1.0.0":
NSString* requiredVersion = #"1.2.0";
NSString* actualVersion = #"1.1.5";
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
// actualVersion is lower than the requiredVersion
}
I'll add my method, which compares strictly numeric versions (no a, b, RC etc.) with any number of components.
+ (NSComparisonResult)compareVersion:(NSString*)versionOne toVersion:(NSString*)versionTwo {
NSArray* versionOneComp = [versionOne componentsSeparatedByString:#"."];
NSArray* versionTwoComp = [versionTwo componentsSeparatedByString:#"."];
NSInteger pos = 0;
while ([versionOneComp count] > pos || [versionTwoComp count] > pos) {
NSInteger v1 = [versionOneComp count] > pos ? [[versionOneComp objectAtIndex:pos] integerValue] : 0;
NSInteger v2 = [versionTwoComp count] > pos ? [[versionTwoComp objectAtIndex:pos] integerValue] : 0;
if (v1 < v2) {
return NSOrderedAscending;
}
else if (v1 > v2) {
return NSOrderedDescending;
}
pos++;
}
return NSOrderedSame;
}
This is an expansion to Nathan de Vries answer to address the problem of 1 < 1.0 < 1.0.0 etc.
First off we can address the problem of extra ".0"'s on our version string with an NSString category:
#implementation NSString (VersionNumbers)
- (NSString *)shortenedVersionNumberString {
static NSString *const unnecessaryVersionSuffix = #".0";
NSString *shortenedVersionNumber = self;
while ([shortenedVersionNumber hasSuffix:unnecessaryVersionSuffix]) {
shortenedVersionNumber = [shortenedVersionNumber substringToIndex:shortenedVersionNumber.length - unnecessaryVersionSuffix.length];
}
return shortenedVersionNumber;
}
#end
With the above NSString category we can shorten our version numbers to drop the unnecessary .0's
NSString* requiredVersion = #"1.2.0";
NSString* actualVersion = #"1.1.5";
requiredVersion = [requiredVersion shortenedVersionNumberString]; // now 1.2
actualVersion = [actualVersion shortenedVersionNumberString]; // still 1.1.5
Now we can still use the beautifully simple approach proposed by Nathan de Vries:
if ([requiredVersion compare:actualVersion options:NSNumericSearch] == NSOrderedDescending) {
// actualVersion is lower than the requiredVersion
}
I made it myself,use Category..
Source..
#implementation NSString (VersionComparison)
- (NSComparisonResult)compareVersion:(NSString *)version{
NSArray *version1 = [self componentsSeparatedByString:#"."];
NSArray *version2 = [version componentsSeparatedByString:#"."];
for(int i = 0 ; i < version1.count || i < version2.count; i++){
NSInteger value1 = 0;
NSInteger value2 = 0;
if(i < version1.count){
value1 = [version1[i] integerValue];
}
if(i < version2.count){
value2 = [version2[i] integerValue];
}
if(value1 == value2){
continue;
}else{
if(value1 > value2){
return NSOrderedDescending;
}else{
return NSOrderedAscending;
}
}
}
return NSOrderedSame;
}
Test..
NSString *version1 = #"3.3.1";
NSString *version2 = #"3.12.1";
NSComparisonResult result = [version1 compareVersion:version2];
switch (result) {
case NSOrderedAscending:
case NSOrderedDescending:
case NSOrderedSame:
break;
}
Sparkle (the most popular software update framework for MacOS) has a SUStandardVersionComparator class that does this, and also takes into account build numbers and beta markers. I.e. it correctly compares 1.0.5 > 1.0.5b7 or 2.0 (2345) > 2.0 (2100). The code only uses Foundation, so should work fine on iOS as well.
Check out my NSString category that implements easy version checking on github; https://github.com/stijnster/NSString-compareToVersion
[#"1.2.2.4" compareToVersion:#"1.2.2.5"];
This will return a NSComparisonResult which is more accurate then using;
[#"1.2.2" compare:#"1.2.2.5" options:NSNumericSearch]
Helpers are also added;
[#"1.2.2.4" isOlderThanVersion:#"1.2.2.5"];
[#"1.2.2.4" isNewerThanVersion:#"1.2.2.5"];
[#"1.2.2.4" isEqualToVersion:#"1.2.2.5"];
[#"1.2.2.4" isEqualOrOlderThanVersion:#"1.2.2.5"];
[#"1.2.2.4" isEqualOrNewerThanVersion:#"1.2.2.5"];
Swift 2.2 Version :
let currentStoreAppVersion = "1.10.2"
let minimumAppVersionRequired = "1.2.2"
if currentStoreAppVersion.compare(minimumAppVersionRequired, options: NSStringCompareOptions.NumericSearch) ==
NSComparisonResult.OrderedDescending {
print("Current Store version is higher")
} else {
print("Latest New version is higher")
}
Swift 3 Version :
let currentStoreVersion = "1.1.0.2"
let latestMinimumAppVersionRequired = "1.1.1"
if currentStoreVersion.compare(latestMinimumAppVersionRequired, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
print("Current version is higher")
} else {
print("Latest version is higher")
}
I thought I'd just share a function I pulled together for this. It is not perfect at all. Please take a look that the examples and results. But if you are checking your own version numbers (which I have to do to manage things like database migrations) then this may help a little.
(also, remove the log statements in the method, of course. those are there to help you see what it does is all)
Tests:
[self isVersion:#"1.0" higherThan:#"0.1"];
[self isVersion:#"1.0" higherThan:#"0.9.5"];
[self isVersion:#"1.0" higherThan:#"0.9.5.1"];
[self isVersion:#"1.0.1" higherThan:#"1.0"];
[self isVersion:#"1.0.0" higherThan:#"1.0.1"];
[self isVersion:#"1.0.0" higherThan:#"1.0.0"];
// alpha tests
[self isVersion:#"1.0b" higherThan:#"1.0a"];
[self isVersion:#"1.0a" higherThan:#"1.0b"];
[self isVersion:#"1.0a" higherThan:#"1.0a"];
[self isVersion:#"1.0" higherThan:#"1.0RC1"];
[self isVersion:#"1.0.1" higherThan:#"1.0RC1"];
Results:
1.0 > 0.1
1.0 > 0.9.5
1.0 > 0.9.5.1
1.0.1 > 1.0
1.0.0 < 1.0.1
1.0.0 == 1.0.0
1.0b > 1.0a
1.0a < 1.0b
1.0a == 1.0a
1.0 < 1.0RC1 <-- FAILURE
1.0.1 < 1.0RC1 <-- FAILURE
notice that alpha works but you have to be very careful with it. once you go alpha at some point you cannot extend that by changing any other minor numbers behind it.
Code:
- (BOOL) isVersion:(NSString *)thisVersionString higherThan:(NSString *)thatVersionString {
// LOWER
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending) {
NSLog(#"%# < %#", thisVersionString, thatVersionString);
return NO;
}
// EQUAL
if ([thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedSame) {
NSLog(#"%# == %#", thisVersionString, thatVersionString);
return NO;
}
NSLog(#"%# > %#", thisVersionString, thatVersionString);
// HIGHER
return YES;
}
My iOS library AppUpdateTracker contains an NSString category to perform this sort of comparison. (Implementation is based off DonnaLea's answer.)
Usage would be as follows:
[#"1.4" isGreaterThanVersionString:#"1.3"]; // YES
[#"1.4" isLessThanOrEqualToVersionString:#"1.3"]; // NO
Additionally, you can use it to keep track of your app's installation/update status:
[AppUpdateTracker registerForAppUpdatesWithBlock:^(NSString *previousVersion, NSString *currentVersion) {
NSLog(#"app updated from: %# to: %#", previousVersion, currentVersion);
}];
[AppUpdateTracker registerForFirstInstallWithBlock:^(NSTimeInterval installTimeSinceEpoch, NSUInteger installCount) {
NSLog(#"first install detected at: %f amount of times app was (re)installed: %lu", installTimeSinceEpoch, (unsigned long)installCount);
}];
[AppUpdateTracker registerForIncrementedUseCountWithBlock:^(NSUInteger useCount) {
NSLog(#"incremented use count to: %lu", (unsigned long)useCount);
}];
Here is the swift 4.0 + code for version comparison
let currentVersion = "1.2.0"
let oldVersion = "1.1.1"
if currentVersion.compare(oldVersion, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedDescending {
print("Higher")
} else {
print("Lower")
}
Glibc has a function strverscmp and versionsort… unfortunately, not portable to the iPhone, but you can write your own fairly easily. This (untested) re-implementation comes from just reading the documented behavior, and not from reading Glibc's source code.
int strverscmp(const char *s1, const char *s2) {
const char *b1 = s1, *b2 = s2, *e1, *e2;
long n1, n2;
size_t z1, z2;
while (*b1 && *b1 == *b2) b1++, b2++;
if (!*b1 && !*b2) return 0;
e1 = b1, e2 = b2;
while (b1 > s1 && isdigit(b1[-1])) b1--;
while (b2 > s2 && isdigit(b2[-1])) b2--;
n1 = strtol(b1, &e1, 10);
n2 = strtol(b2, &e2, 10);
if (b1 == e1 || b2 == e2) return strcmp(s1, s2);
if (n1 < n2) return -1;
if (n1 > n2) return 1;
z1 = strspn(b1, "0"), z2 = strspn(b2, "0");
if (z1 > z2) return -1;
if (z1 < z2) return 1;
return 0;
}
If you know each version number will have exactly 3 integers separated by dots, you can parse them (e.g. using sscanf(3)) and compare them:
const char *version1str = "1.0.1";
const char *version2str = "1.2.5";
int major1, minor1, patch1;
int major2, minor2, patch2;
if(sscanf(version1str, "%d.%d.%d", &major1, &minor1, &patch1) == 3 &&
sscanf(version2str, "%d.%d.%d", &major2, &minor2, &patch2) == 3)
{
// Parsing succeeded, now compare the integers
if(major1 > major2 ||
(major1 == major2 && (minor1 > minor2 ||
(minor1 == minor2 && patch1 > patch2))))
{
// version1 > version2
}
else if(major1 == major2 && minor1 == minor2 && patch1 == patch2)
{
// version1 == version2
}
else
{
// version1 < version2
}
}
else
{
// Handle error, parsing failed
}
To check the version in swift you can use following
switch newVersion.compare(currentversion, options: NSStringCompareOptions.NumericSearch) {
case .OrderedDescending:
println("NewVersion available ")
// Show Alert Here
case .OrderedAscending:
println("NewVersion Not available ")
default:
println("default")
}
Hope it might be helpful.
Here is a recursive function that do the works with multiple version formatting of any length. It also works for #"1.0" and #"1.0.0"
static inline NSComparisonResult versioncmp(const NSString * a, const NSString * b)
{
if ([a isEqualToString:#""] && [b isEqualToString:#""]) {
return NSOrderedSame;
}
if ([a isEqualToString:#""]) {
a = #"0";
}
if ([b isEqualToString:#""]) {
b = #"0";
}
NSArray<NSString*> * aComponents = [a componentsSeparatedByString:#"."];
NSArray<NSString*> * bComponents = [b componentsSeparatedByString:#"."];
NSComparisonResult r = [aComponents[0] compare:bComponents[0] options:NSNumericSearch];
if(r != NSOrderedSame) {
return r;
} else {
NSString* newA = (a.length == aComponents[0].length) ? #"" : [a substringFromIndex:aComponents[0].length+1];
NSString* newB = (b.length == bComponents[0].length) ? #"" : [b substringFromIndex:bComponents[0].length+1];
return versioncmp(newA, newB);
}
}
Test samples :
versioncmp(#"11.5", #"8.2.3");
versioncmp(#"1.5", #"8.2.3");
versioncmp(#"1.0", #"1.0.0");
versioncmp(#"11.5.3.4.1.2", #"11.5.3.4.1.2");
Based on #nathan-de-vries 's answer, I wrote SemanticVersion.swift for comparing Semantic Version, and here is the test cases.