Is it valid to ask an NSNumber for an NSDecimal value whe the NSNumber was initialized with an Integer? - iphone

Example: I have an NSInteger and I wrap that into an NSNumber object. Now I want to have an NSDecimal with the value of that NSInteger.
So could I ask:
NSDecimal myDecimalFromMyInteger = [myNSNumberObject decimalValue];
Or is this problematic on some way? Or must I always ask for the exact same value as I used to create the NSNumber object?

This is perfectly fine. From the docs:
"Returns the receiver’s value, expressed as an NSDecimal structure. ... The receiver’s value, expressed as an NSDecimal structure. The value returned isn’t guaranteed to be exact for float and double values."
As long as you're aware of the inherent precision implications, this is a perfectly legal and sane thing to do.

Related

NSNumber stored in NSUserDefaults

Something weird just happened.
I stored a NSNumber with an unsigned long long value in NSUserDefaults. When I retrieve it, the value just changed. It seems that system thinks the number is long long instead of unsigned long long.
What's worse is that when I compare the number retrieved from UserDefaults with the original number, the result is NotEqual!
what's wrong with the code? Thank you!
static NSString * const NumberKey = #"MyNumber";
unsigned long long value = 15908045869032883218ULL;
if ([[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] == nil) {
NSNumber *number = [NSNumber numberWithUnsignedLongLong:value];
[[NSUserDefaults standardUserDefaults] setObject:number forKey:NumberKey];
NSLog(#"Original Number:%#", number); // 15908045869032883218, right
}
NSNumber *number = [[NSUserDefaults standardUserDefaults] objectForKey:NumberKey];
NSLog(#"Current Number:%#", number); // -2538698204676668398, weird
NSLog(#"Current Value:%llu", [number unsignedLongLongValue]); // 15908045869032883218, right
NSLog(#"%d", [number isEqualToNumber:[NSNumber numberWithUnsignedLongLong:value]]); // 0
NSLog(#"%d", [number unsignedLongLongValue] == value); // 1
To further answer your question. If you look in the documentation for NSNumber's isEqualToNumber: function you will notice the following line,
Two NSNumber objects are considered equal if they have the same id values or if they have equivalent values
it's important you understand this. In your code you are asking is my NSNumber object "number" equal to "value", you are not asking does the numerical value stored within my NSNumber object "number" equal the numerical value stored within my NSNumber object "value".
The last line of code you have written shows that in fact your NSNumber's numerical values are in fact equal.
NSLog(#"%d", [number unsignedLongLongValue] == value); //1
So you are correctly storing and retrieving the values, you should be using the == comparison method with NSNumber objects stored numerical values (ie intValue == intValue, unsignedLongLongValue == unsignedLongLongValue) and not comparing their object id's together.
As for this line of code
NSLog(#"Current Number:%#", number); // -2538698204676668398, weird
This is not weird, this is perfectly normal, as you have told NSLog to print out an NSObject representation of 'number'. I'm not 100% certain but I believe that NSNumber's - ( NSString * ) description function defaults to return an unsigned int value for the numerical value it contains. This is why you are getting the large negative number returned. You may want to look at NSNumber's - (NSString *)descriptionWithLocale:(id)aLocale function to print out the data in a more logical for for you, or you could use
NSLog(#"Current Number:%llu", [number unsignedLongLongValue]);
Which will give you the right answer.
EDIT:
Further to this, after looking into the issue what is happening is that on recollection of your NSNumber object from UserDefaults it's original number type is not being preserved (this information is highlighted in the documentation for NSNumber in the overview section)
(Note that number objects do not necessarily preserve the type they are created with.)
You can see this yourself if you log the following after retrieving "number" from user defaults (add this to the end of the code you have in your question) and have a look at the encoding values shown here
NSLog(#"%s", [number objCType]); //This will log out q
NSLog(#"%s", [[NSNumber numberWithUnsignedLongLong:value] objCType]); //this will log out Q
The difference between Q and q is that Q is an unsigned value... hence why you are having issues with the isEqualToNumber: function as the number types are different.
If you are so dead set on using the iSEqualToNumber: function to compare values then you could implement this to retrieve your value from NSUserDefaults.
NSNumber *number = [NSNumber numberWithUnsignedLongLong:[[NSUserDefaults standardUserDefaults] objectForKey:NumberKey] unsignedLongLongValue]];
You could look at using the NSNumber compare: function to see if the returned value is NSOrderedSame however this will not work for comparing unsigned vs signed values of the same type so in your situation I'd use the above as retrieving the data from NSUserDefaults is stripping the "signedness" of your number.
At the end of the day if you want to store NSNumber into NSUserDefaults this code works for me even for large integers like: 881217446193276338
To save:
[[NSUserDefaults standardUserDefaults] setObject:self.myUser.sessionid forKey:#"sessionid"];
[[NSUserDefaults standardUserDefaults] synchronize];
To recover:
self.myUser.sessionid = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:#"sessionid"];
It's storing it correctly, nothing is wrong with your code except:
NSLog(#"Current Number:%#", number);
Here number is a non-string object, you might think of it as a wrapper for a numerical primitive. Or you might think that NSNumber instances objectify a primitive type.
What you need is some thing like:
NSLog(#"Current Number:%#", [number stringValue]);
Here is a speculative answer:
The NSNumber documentation states that:
(Note that number objects do not necessarily preserve the type they are created with.) .
So it must be using a different internal storage for this type, and only gives you the correct value when you specifically ask for the unsigned long long value. The description method, which is called in your NSLog statement, may be defaulting to a different representation type.
And / or there may be some quirk of the unarchiver that is preventing the isEqualToNumber method working on the value returned from defaults. If you do that comparison between two NSNumbers created in the same scope, does it work? The correct value is definitely in there somewhere given your last statement returns true.

doubleValue does not convert the object to a double value correctly at all times

I have an NSArray in which I would like to store double values. I defined it as follow
NSMutableArray *setOfDoubles;
I add the elements as follow
NSNumber *num;
num = [NSNumber numberWithDouble:someDouble];
[setOfDoubles addObject:num];
And I read the element as follow
num = [setOfDoubles lastObject];
[setOfDoubles removeLastObject];
doubleValueToUse = [num doubleValue];
My problem is sometimes (Not always), for example when num (as an object) is 5.1, doubleValueToUse (as a double value) is 5.099999999999996. The way I figured num (as an object) is 5.1 is that I debug and when I am hovering the mouse on top of num on the line num = [setOfDoubles lastObject]; it shows 5.1 but after doubleValue conversion it becomes the number I mentioned. Does anybody know why is this happening?
Not every number can be accurately represented using a float variable. For example, you can't precisely represent, say, 1/3 using a finite number of digits in our common decimal (base-10) system, but in ternary (base-3), it would be just 0.1. Similarly, the numbers you can write with a finite number of digits in decimal, may not necessarily have the finite number of digits in their binary representation, hence the error.
A few links on the topic if you are interested:
http://floating-point-gui.de/basic/
http://www.mathworks.com/support/tech-notes/1100/1108.html
http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html
This is normal for float values.
If you want to save initial (same) representation of float numbers in all places of your code, you can save them, for example, in NSString. When you will need float number you will just write [NSString floatValue];. But it is not effective if you have large amount of float values.

Objective C convert number to NSNumber

Why doesn't this please the compiler? Casting is supposed to work like in C as I can read here How to cast an object in Objective-C.
[p setAge:(NSNumber*)10];
where
- (NSNumber*) age {
return _age;
}
- (void) setAge: (NSNumber*)input {
[_age autorelease];
_age = [input retain];
}
In a sort of symmetry with your earlier question, NSNumber is an object type. You need to create it via a method invocation such as:
[p setAge:[NSNumber numberWithInt:10]];
Your current code is attempting simply to cast the arbitrary integer 10 to a pointer. Never do this: if the compiler didn't warn you about it, you would then be trying to access some completely inappropriate bytes at memory location 10 as if they were an NSNumber object, which they wouldn't be. Doing that stuff leads to tears.
Oh, and just to preempt the next obvious issues, remember that if you want to use the value in an NSNumber object, you need to get at that via method calls too, eg:
if ( [[p age] intValue] < 18 )
...
(NSNumber is immutable, and I think it is implemented such that identical values are mapped to the same object. So it is probably possible to get away with direct pointer comparisons for value equality between NSNumber objects. But please don't, because that would be an inappropriate reliance on an implementation detail. Use isEqual instead.)
Today it is also possible to do it using shorthand notation:
[p setAge:#(10)];
Use this:
[p setAge:[NSNumber numberWithInt:10]];
You can't cast an integer literal like 10 to a NSNumber* (pointer to NSNumber).
NSNumber *yourNumber = [NSNumber numberWithInt:your_int_variable];
Because NSNumber is an object and "10" in a primitive integer type, much like the difference between int and Integer in Java. You, therefore, need to call its initialiser:
[p setAge:[NSNumber numberWithInt:10]

Convert a string of numbers to a NSTimeInterval

I know I must be over-complicating this because it NSTimeInterval is just a double, but I just can't seem to get this done properly since I have had very little exposure to objective c. the scenario is as follows:
The data im pulling into the app contains two values, startTime and endTime, which are the epoch times in milliseconds. The variables that I want to hold these values are
NSTimeInterval *start;
NSTimeInterval *end;
I decided to store them as NSTimeIntervals but im thinking that maybe i ought to store them as doubles because theres no need for NSTimeIntervals since comparisons can just be done with a primitive. Either way, I'd like to know what I'm missing in the following step, where I try to convert from string to NSTimeInterval:
tempString = [truckArray objectAtIndex:2];
tempDouble = [tempString doubleValue];
Now it's safely stored as a double, but I can't get the value into an NSTimeInterval. How should this be accomplished? Thanks
You don't have to cast, you can just write this:
NSTimeInterval timeInterval = [[truckArray objectAtIndex:2] doubleValue];
The cast is needless, and extra casts just make your source code harder to update and change in the future because you've told the compiler not to type-check your casted expressions.
The variables that I want to hold these values are NSTimeInterval *start; NSTimeInterval *end;
Careful, NSTimeInverval is a typedef for a primitive C type, it is not an Objective-C object. I don't think you actually need pointers to these types in this scenario, so you should declare them like this:
NSTimeInverval start;
NSTimeInterval end;
You could be getting errors because in C, you cannot convert floating-point types to pointer-types.

Secure and valid way for converting a "float integer" into a natural integer?

I have a CGFloat that contains only "integers", in the meaning that it actually wants to represent integers only but due to float inprecision it may happen that it internally has a 23.00000000000000000000000142 or something like that. I try to feed an NSDecimalNumber with clean input, so I need to make sure this is truly a naked integer with no dirt. Well, I think this is a good way, but maybe I am wrong:
NSInteger *intVal = floatVal;
That would just get rid of those fragmental parts at the end of the tale, right? Or is there a more secure way to convert it into a true integer?
If you just want to take the integer part of a float then I believe you can just do the following:
CGFloat myfloat = 23.0000000000142f;
int myInt = (int) myfloat;