Storing and retrieving unsigned long long value to/from NSString - objective-c

I have an unsigned long long value which I want to store into an NSString and retrieve from the string.
Initially I have the value in an NSNumber and I am using this to get the string
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
where myNum is an NSNumber.
To get back the NSNumber from the NSString I have to first get the unsigned long long value. But there is no method in the NSString class to do that (we just have one for getting the long long value, not the unsigned long long value).
Can someone please tell me how I can get back the value into an NSNumber variable.
Thanks.

There are a lot of ways to accomplish this. The following is the most pragmatic:
NSString *numStr = [NSString stringWithFormat:#"%llu", [myNum unsignedLongLongValue]];
// .. code and time in between when numStr was created
// .. and now needs to be converted back to a long long.
// .. Therefore, numStr used below does not imply the same numStr above.
unsigned long long ullvalue = strtoull([numStr UTF8String], NULL, 0);
This makes a few reasonable assumptions such as numStr will only contain numeric digits and it contains a 'valid' unsigned long long value. A drawback to this approach is that UTF8String creates what essentially amounts to [[numStr dataUsingEncoding:NSUTF8StringEncoding] bytes], or in other words something along the lines of 32 bytes of autoreleased memory per call. For the vast majority of uses, this is no problem what-so-ever.
For an example of how to add something like unsignedLongLongValue to NSString that is both very fast and uses no autoreleased memory as a side effect, take a look at the end of my (long) answer to this SO question. Specifically the example implementation of rklIntValue, which would require only trivial modifications to implement unsignedLongLongValue.
More information regarding strtoull can be found in its man page.

Related

objective-c NSString to 0x%04x format

NSString *testValue = #"3141";
I want to convert "3141" to 0x0c45, use hexadecimal format(0x%04x).
like this:
printf("0x%04x", testValue);
The value printed out is:
0x0c45
But I don't know how to save it, how can I do it?
====================================================
NSString *testValue = #"3141";
NSUInteger unsignedValue = (NSUInteger)testValue.integerValue;
NSString *formatted = [NSString stringWithFormat:#"0x%04X", unsignedValue];
The result of this transfer seems to be incorrect. The result is 0x0000. Let me paste the source code as follows:
UInt16 myValue = 3141;
printf("0x%04x", myValue);
The result printed is 0x0c45. But what I need is not to print, but to save the value 0x0c45.
If you take a look at the parameters that printf expects, there's a table of format specifiers that printf understands, and the input that printf expects to correspond to those format specifiers. Specifically, the x/X specifiers:
converts an unsigned integer into hexadecimal representation hhhh
When printf tries to format testValue, it is expecting an unsigned integer of some variety (unsigned int, unsigned long, etc.), and the testValue you pass in is an NSString * (a pointer to an NSString). The pointer value itself is being interpreted as an unsigned integer, and printed out by printf.
In order to get printf to print the value you actually expect, you will first need to get the integer value of the string (either via integerValue, or a different method), and then pass that to printf. For example:
NSString *testValue = #"3141";
NSUInteger unsignedValue = (NSUInteger)testValue.integerValue;
printf("0x%04X", unsignedValue);
(Note that in a production environment, you'll probably want to use something more failure tolerant than just integerValue, and you'll need to be aware of the domain of how large testValue might need to be, the size of the storage of unsignedValue, etc.)
Update: If you'd like to get the result of formatting a value in this way beyond just printing it, you can use something like +[NSString stringWithFormat:] to get the string value. This method accepts the same specifiers that printf does, so you can use it in the same way:
NSString *testValue = #"3141";
NSUInteger unsignedValue = (NSUInteger)testValue.integerValue;
NSString *formatted = [NSString stringWithFormat:#"0x%04X", unsignedValue];
// Do anything you want with `formatted`.

Why is longLongValue returning the incorrect value

I have a NSDictionary that contains a key with a value of 4937446359977427944. I try and get the value of it as a long long and get 4937446359977427968 back?
NSLog(#"value1 = %#", [dict objectForKey"MyKey"]); // prints 4937446359977427944
long long lv = [dict objectForKey:#"MyKey"] longLongValue];
NSLog(#"value2 = %lld", lv); // prints 4937446359977427968
Doing:
NSLog(#"%lld", [#"4937446359977427944" longLongValue]); // prints 4937446359977427944
I'm assuming it is some kind of round off issue since the lower bits seems to be cleared, I just don't know how to stop it (or why it's happening).
The dictionary is being created using NSJSONSerialization and the JSON object does (correctly) contain a "MyKey": 4937446359977427944 entry and the dict object is correct.
The value being held in the NSDictionary is a NSDecimalNumber
Is something being convert to a float behind the scenes?
NSDecimalValue is not stored as a double, it's a 64 bits unsigned integer mantissa, an 8 bit signed integer exponent of base 10, and a sign bit.
The problem is that an exact value of an NSDecimalValue is only representable as ... an NSDecimalValue.
You can get an approximate 64 bits IEE754 value with method doubleValue.
When you try to use longLongValue you effectively get the result of casting to a long long int the approximate IEE754 value.
You may or may not consider it a bug in the implementation of NSDecimalValue (and eventually file a radar and ask Apple to use a different conversion routine). But strictly speaking this is not a bug: it's a design decision.
You should think of NSDecimalValue as a sort of floating point decimal. In fact it's very similar to a software implementation of what IEEE754 would call an extended precision floating point decimal number, except that it does not conform to that definition (because it does not have an exponent supporting at least values between −6143 and +6144 and because it does not support NANs and infinites).
In other words, it's not an extended implementation of an integer, it's an extended (but lacking NANs and infinites) implementation of a double. The fact that Apple natively only provides an approximate conversion to double (implying that the conversion to long long int may or may not be exact for any value that exceed 53 bits of precision) is not a bug.
You may or may not want to implement a different conversion yourself (with a category).
Another possible point of view is to consider the problem being a bug in the JSon implementation you used. But this is also highly debatable: it gave you a NSDecimalValue and that's arguably a correct representation. Either you operate with the NSDecimalValue or you are responsible for any conversion of it.
I'm not sure if your are interested in a simple solution or just looking into the details of why the loss of precision takes place.
If you are interested in a simple answer: -[NSDecimalNumber description] products a string with the value, and -[NSString longLongValue] converts a string into a long long
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString:#"4937446359977427944"];
long long longLongNumber = [[decimalNumber description] longLongValue];
NSLog(#"decimalNumber %# -- longLongNumber %lld", decimalNumber, longLongNumber);
outputs
2014-04-16 08:51:21.221 APP_NAME[30458:60b] decimalNumber 4937446359977427944 -- longLongNumber 4937446359977427944
Final Note
[decimalNumber descriptionWithLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"]] may be more reliable is your app supports multiple locales.
For anyone interested in quick solution to the problem, as per Analog File proper answer:
long long someNumber = 8204064638523577098;
NSLog(#"some number lld: %lld", someNumber);
NSNumber *snNSNumber = [NSNumber numberWithLongLong:someNumber];
NSLog(#"some number NSNumber: %#", snNSNumber);
NSString *someJson = #"{\"someValue\":8204064638523577098}";
NSDictionary* dict = [NSJSONSerialization
JSONObjectWithData:[someJson dataUsingEncoding:NSUTF8StringEncoding]
options:0
error:nil];
NSLog(#"Dict: %#", dict);
NSLog(#"Some digit out of dict: %#", [dict objectForKey:#"someValue"]);
NSLog(#"Some digit out of dict as lld: %lld", [[dict objectForKey:#"someValue"] longLongValue]);
long long someNumberParsed;
sscanf([[[dict objectForKey:#"someValue"] stringValue] UTF8String], "%lld", &someNumberParsed);
NSLog(#"Properly parsed lld: %lld", someNumberParsed);
Results in:
2014-04-16 14:22:02.997 Tutorial4[97950:303] some number lld:
8204064638523577098
2014-04-16 14:22:02.998 Tutorial4[97950:303] some number NSNumber:
8204064638523577098
2014-04-16 14:22:02.998 Tutorial4[97950:303] Dict: { someValue =
8204064638523577098; }
2014-04-16 14:22:02.998 Tutorial4[97950:303] Some digit out of dict:
8204064638523577098
2014-04-16 14:22:02.999 Tutorial4[97950:303] Some digit out of dict as
lld: 8204064638523577344
2014-04-16 14:22:02.999 Tutorial4[97950:303] Properly parsed lld:
8204064638523577098

Multi-character character sequence in 3rd party library

I'm using a 3rd party library for an iOS project I work on, and I'm down to one warning left in the project, namely on this line of code
[NSNumber numberWithUnsignedLongLong:'oaut']
And the warning is
Multi-character character constant
I suck at C, so I don't know how to fix this, but I'm sure the fix is relatively easy. Help?
EDIT: More context.
#implementation MPOAuthCredentialConcreteStore (KeychainAdditions)
- (void)addToKeychainUsingName:(NSString *)inName andValue:(NSString *)inValue {
NSString *serverName = [self.baseURL host];
NSString *securityDomain = [self.authenticationURL host];
// NSString *itemID = [NSString stringWithFormat:#"%#.oauth.%#", [[NSBundle mainBundle] bundleIdentifier], inName];
NSDictionary *searchDictionary = nil;
NSDictionary *keychainItemAttributeDictionary = [NSDictionary dictionaryWithObjectsAndKeys: (id)kSecClassInternetPassword, kSecClass,
securityDomain, kSecAttrSecurityDomain,
serverName, kSecAttrServer,
inName, kSecAttrAccount,
kSecAttrAuthenticationTypeDefault, kSecAttrAuthenticationType,
[NSNumber numberWithUnsignedLongLong:"oaut"], kSecAttrType,
[inValue dataUsingEncoding:NSUTF8StringEncoding], kSecValueData,
nil];
if ([self findValueFromKeychainUsingName:inName returningItem:&searchDictionary]) {
NSMutableDictionary *updateDictionary = [keychainItemAttributeDictionary mutableCopy];
[updateDictionary removeObjectForKey:(id)kSecClass];
SecItemUpdate((CFDictionaryRef)keychainItemAttributeDictionary, (CFDictionaryRef)updateDictionary);
[updateDictionary release];
} else {
OSStatus success = SecItemAdd( (CFDictionaryRef)keychainItemAttributeDictionary, NULL);
if (success == errSecNotAvailable) {
[NSException raise:#"Keychain Not Available" format:#"Keychain Access Not Currently Available"];
} else if (success == errSecDuplicateItem) {
[NSException raise:#"Keychain duplicate item exception" format:#"Item already exists for %#", keychainItemAttributeDictionary];
}
}
}
EDIT 2: They were attempting to meet the requirements of this by creating that NSNumber:
#constant kSecAttrType Specifies a dictionary key whose value is the item's
type attribute. You use this key to set or get a value of type
CFNumberRef that represents the item's type. This number is the
unsigned integer representation of a four-character code (e.g.,
'aTyp').
In C and Obj-C the single-quote ' is used only for single-character constants. You need to use the double-quote: "
Like so:
[NSNumber numberWithUnsignedLongLong:"oaut"]
That covers the warning, but there's also a semantic issue here. Although a single character constant, such as 'o', can be treated as an integer (and can be promoted to an unsigned long long), a "string" (char * or char []) cannot, which means you can't use "oaut" as an argument to numberWithUnsignedLongLong:
Update:
I guess the four-character code is supposed to be treated as an integer, i.e., the 8 bits of each char put in place as if they together were a 32-bit int:
char code[] = "oaut";
uint32_t code_as_int = code[0] | (code[1] << 8) | (code[2] << 16) | (code[3] << 24);
[NSNumber numberWithUnsignedLongLong:code_as_int]
although I'm not sure which endianness would be expected here, nor why this is calling for an unsigned long long, unless just to be certain there are enough bits.
Rudy's comment, now that I think of it, is correct -- multi-character constants are allowed by some compilers for exactly this purpose (it is "implementation-defined" behavior).
'oaut' (single quotes) is a character, so the compiler tries to interpret it as a multi-byte character but can't make any sense of it. That explains the error message.
I guess that if you gave a proper string, like #"oaut", you'd get another error message, since numberWithUnsignedLongLong: expects an unsigned long long, not a string or a character. Are you trying to pass a variable with the name "oaut"? If so, use
[NSNumber numberWithUnsignedLongLong: oaut];
If not, then please explain what "oaut" is.
Edit
'oaut' may actually be the original value. There are/were multi-character character constants in C. Using a (4 byte) char, used as int and promoted to unsigned long long would then be possible. This must be old code. It seems such code was accepted by CodeWarrior.
Assuming that really a multi-char char const was meant, 'oaut' looks like a "magic number" and this value was chosen because it is the beginning of "oauth". I guess it should either be value 0x6F617574 or 0x7475616F.
#Josh Caswell 's answer is partially right, the simplest and "official" solution is:
[NSNumber numberWithUnsignedInt:'oaut']
unsigned int's length is 32-bit in both 32-bit and 64-bit cpu, there's a practical example from Apple: https://developer.apple.com/library/ios/samplecode/CryptoExercise/Listings/Classes_SecKeyWrapper_m.html

need help converting an MPMediaItemPropertyPersistentID to a string and back again

I'm getting the ID of a media item from the MPMediaPickerController. According to the documentation, this value is an NSNumber object containing a uint64_t (unsigned long long). I would like to convert it to an NSString for saving in my data model. However, when I convert it to a string its value changes, and when I convert it back to a number it changes again! Obviously I'm not understanding something about these data types. Can you help?
MPMediaItem *mediaPicked;
// set mediaPicked to an MPMedia item using MPMediaPickerController...
NSLog(#"id as number: %qu", [mediaPicked valueForProperty:MPMediaItemPropertyPersistentID]); // outputs 566042331449280
NSLog(#"id as string: %#", [[mediaPicked valueForProperty:MPMediaItemPropertyPersistentID] stringValue]); // outputs 16204893883745507648
NSLog(#"id as number: %qu", [[[mediaPicked valueForProperty:MPMediaItemPropertyPersistentID] stringValue] longLongValue]); // outputs 9223372036854775807
If I try to play the media item before this conversion, it always works. But if I try to play the media item after this conversion, only about half of the media items I've tried work. So some ID values survive the conversion and some don't.
You are starting by saying this:
NSLog(#"id as number: %qu", [mediaPicked valueForProperty:MPMediaItemPropertyPersistentID]); // outputs 566042331449280
But that's wrong. %qu means "this thing is an unsigned long long". But this thing is not an unsigned long long. It's an object! It's an NSNumber wrapped around an unsigned long long. You are lying to NSLog, so you're getting garbage output in your very first statement.
Now, try this on your own machine:
uint64_t x = 16204893883745507648ULL;
NSLog(#"%qu", x);
NSNumber* n = [NSNumber numberWithUnsignedLongLong:x];
NSLog(#"%#", n);
NSLog(#"%#", [n stringValue]);
All of those NSLog statements give the same result - because they are all correct formulations, unlike the one you started with. So, those NSLog statements show you the kind of thing you ought to be saying.
Now, you might think: Oh, great, so I can get from an NSNumber to an NSString with stringValue after all. Yes, but you can't get back again. We cannot get from [n stringValue] to a correct NSNumber by using longlongValue, because a long long is not an unsigned long long. There is no unsignedLonglongValue. So you can't get there from here.
So what's the right thing to do? Don't convert at all! You've got an NSNumber, it's valid, just keep it and use it. An NSNumber is a value you can store in your model. (For example, it can go into a dictionary as a value or as a key, it can be a value in user defaults, and so on.)

Converting NSString to a decimal

I need to change the code below to make "intAmount" a decimal or an integer (i.e. a person can enter .10 or 1) in my uitextfield. The last line "myProduct" has to be a decimal not an integer and return the product in the format "18.00" for example. Can someone help someone help me alter my code snippit for this?
//amt has to be converted into a decimal value its a NSString now
NSInteger intAmount = [amt intValue];
//where total gets updated in the code with some whole (integer) value
NSInteger total=0;
//Change myProduct to a decimal with presicion of 2 (i.e. 12.65)
NSInteger myProduct=total*intAmount;
THIS DOESN'T WORK
NSDecimalNumber intAmount = [amt doubleValue];
//Keep in mind totalCost is an NSInteger
NSDecimalNumber total=totalCost*intAmount;
Use doubleValue instead of intValue to get the correct fractional number out of your text field. Put it in a variable of type double rather than NSInteger. Then use the format %.2g when you print it out and it will look like you want it to.
If you need to track decimal values explicitly, you can use NSDecimalNumber. However, if all you're doing is this one operation, Carl's solution is most likely adequate.
If you have a string representation of a real number (non-integer), you can use an NSScanner object to scan it into a double or float, or even an NSDecimal structure if that is your true intention (the NSDecimal struct and NSDecimalNumber class are useful for containing numbers that can be exactly represented in decimal).
NSString *amt = #"1.04";
NSScanner *aScanner = [NSScanner localizedScannerWithString:amt];
double theValue;
if ([aScanner scanDouble:&theValue])
{
// theValue should equal 1.04 (or thereabouts)
}
else
{
// the string could not be successfully interpreted
}
The benefit to using a localised NSScanner object is that the number is interpreted based on the user's locale, because “1.000” could mean either one-thousand or just one, depending on your locale.