arc4random in NSNumber giving negative values? - objective-c

I'm creating a random number and storing it in a NSNumber object like this:
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(2^32-1)];
I also tried:
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(4294967295)];
NSNumber *index = #(arc4random_uniform(4294967295));
At some point I'm also assigning the number 1 like this:
NSNumber *index = #(1);
This should give me only positive numbers.
Later on, I print out these numbers like this:
NSString *string = [NSString stringWithFormat:#"%#", index];
This gives me some negative values for the random numbers and 1 is being printed as 1. So I though maybe if I do:
NSString *string = [NSString stringWithFormat:#"%u", index.unsignedIntValue];
I'll get only positive numbers - which I do - but now 1 is suddenly being printed as some large positive number, also.
What's going on here? How can I correctly store a u_int32 (which arc4random returns) in a NSNmber and make sure that they are only positive?

Use
NSNumber *index = [NSNumber numberWithUnsignedInt:arc4random_uniform(exp2(32)-1)];
I never get any negative numbers. arc4random_uniform(x) always returns a number between 0 and x, and the stringvalue of the NSNumber generated from it is correct.
EDIT: replaced exp2(31) with exp2(32)

You said in a comment that the index is stored in a Core Data entity as an "Integer 32" attribute, and I assume that is where the problem comes from.
Core Data dynamically generates getter and setter methods for all attributes (and relationships) of managed object classes. These accessor methods are different from the "usual" #synthesized accessor methods which are backed up by an instance variable.
For an "Integer 32" attribute, Core Data uses a (signed) integer for the attribute, and when you set a value, it is just cast or truncated to int. Example:
e.index = [NSNumber numberWithUnsignedInt:0xFFFFFFF0U];
// This would give the same result:
// e.index = [NSNumber numberWithLongLong:0x1234FFFFFFF0LL];
NSNumber *val = e.index;
NSLog(#"value=%#, type=%s", val, [val objCType]);
// Output: value=-16, type=i
The output type=i shows that the value contains an int.
If you need unsigned integers in the range 0 .. 2^32-1, then you can either (as you already did) use unsignedIntValue:
unsigned x = [val unsignedIntValue];
or store the attribute as "Integer 64".
Remarks:
I am fairly sure that this is not a problem of arc4random_uniform.
In your first code example arc4random_uniform(2^32-1), you should note that ^ is exclusive-or, not exponentiation.

Related

discussing functionality of id's stringValue function?

I got a NSMutableArray object with int values
and I can get a certain value via :
int *v0=[[[arrayObj objectAtIndex:0] intValue];
there is no problem.
But
I got a NSMutableArray object with NSString values
and I cannot get a certain value via :
NSString *v0=[[[arrayObj objectAtIndex:0] stringValue];
//raises error
I want to learn and understand exactly what stringValue for... and why this error occurs ?
NSString *v0=[arrayObj objectAtIndex:0];
works as expected.I asusme its some kind of pointer with null terminated so it can leech value.
Im not sure this line is also unicode/encoded string safe code.
in conclusion:
want to know the purpose of stringValue with some lines o code snippets
I got a NSMutableArray object with int values
That's not possible, Cocoa arrays always contain objects. You probably have an array of NSNumber objects that wrap the integers, like:
NSArray *arrayOfNumbers = #[#1, #2, #3];
NSNumber objects have an intValue method, so this works:
int value = [arrayOfNumbers[0] intValue];
On the other hand when you have an array of strings ...
NSArray *arrayOfStrings = #[#"1", #"2", #"3"];
... you want to access individual elements directly, without converting the string object to something else:
NSString *element = arrayOfStrings[0];
NSString objects do not understand the stringValue method:
[arrayOfStrings[0] stringValue]; // crash: does not recognize selector
Back at the beginning, our NSNumber objects from the first array do understand stringValue. You can use it to convert the number to a string:
NSString *intString = [arrayOfNumbers[0] stringValue];
To make the confusion perfect, NSString also understand the intValue message:
int value = [arrayOfStrings[0] intValue];
Here intValue means to try to convert the string to a plain C int value.
The error you will be getting (but failing to post with your question) will be Unknown selector sent to instance and this is because NSString doesn't have a stringValue method.
The approach you suggest is correct:
NSString *v0 = [arrayObj objectAtIndex:0];
EDIT (prompted by #Answerbot's answer):
The reason you are confused is that [NSString intValue] is used to convert the string value to an integer, as long as the string represents an integer (i.e. #"123"). However you don't need this for string as the object is already a string. It's therefore not provided.

Acquiring NSInteger Value

I am trying to grab the value of an NSInteger and set a UILabel value to it. The code is as follows:
counter_.text = [NSString stringWithFormat:#"%#", [counterAmount intValue]];
where counter_ is my label and counterAmount is my NSInteger. I keep getting a "Receiver type 'NSInteger *' (aka 'int *') is not 'id' or interface pointer, consider casting it to id.
I'm not quite sure how to understand this. I appreciate of your help.
intValue is an NSNumber method that returns a C primitive value. %# prints objects. In order to output a C primitive value you have to supply the type in the formatting string. %d is the type for signed integer.
As per the question, and as pointed out to me quite correctly by borrden below, the type being dealt with here is NSInteger rather than NSNumber. In iOS NSInteger is a typedef of int, so you're dealing directly with a primitive type. The NS prefix does not mean that its an object.
Since counterAmount is a NSInteger, you can not use intValue on it (since it isn't an object, you can't send any messages to it, actually).
Instead, use:
counter_.text = [NSString stringWithFormat:#"%d", counterAmount];
Now that being said, if you are displaying this value to a user you should really be using a number formatter so that it is formatted the way that they have set it up to display in the settings app:
NSNumber *number = [NSNumber numberWithInt:counterAmount];
NSNumberFormatter *formatter = [NSNumberFormatter new];
formatter.numberStyle = NSNumberFormatterDecimalStyle; // Or whichever style is appropriate for your number.
counter_.text = [formatter stringFromNumber:number];

KVC - about data type

this is the code:
NSNumber *taskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
self.taskList is an NSArray which filled with core data fetch request in ViewController's viewDidLoad method.
the taskId is: 1
the intTaskId is: 269303816
In actually, the value stored in core data is: 1
below is my questions:
1, I am confused why the NSInteger incorrect?
2, Should I have to replace NSInteger with NSNumber to avoid any other problems?
NSNumber is an object, whereas NSInteger is simply a typedef for a primitive (non-object) type (like int). NSInteger is not a subclass of NSNumber. Core Data returns numbers as instances of NSNumber. You're getting the weird NSInteger value because it's pointing to an object of type NSNumber but attempting to print it as if it were just an integer.
You'll need to replace NSInteger with NSNumber to avoid any problems. You could also use the intValue method on NSNumber to get back an NSInteger:
NSNumber *objTaskId = [[self.taskList objectAtIndex:indexPath.row] valueForKey:#"identity"];
NSInteger *intTaskId = [objTaskId intValue];
You'll need to do this if you want to do comparisons (greater than, equal too, smaller than) or arithmetic (you can't add an NSNumber to another NSNumber or an NSNumber to a primitive type like an int or float).

What's the real type? NSString or NSNumber

I have a NSDictionary that contains data converted from json data, like {"message_id":21}.
then I use NSNumber *message_id = [dictionary valueForKey:#"message_id"] to get the data.
but when I use this message_id,
Message *message = [NSEntityDescription ....
message.messageId = message_id;
I got the runtime error, assigning _NSCFString to NSNumber,
so I have to use NSNumberFormatter to do the conversion.
NSString *messageId = [dictionary valueForKey:#"message_id"];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterNoStyle];
message.messageId = [f numberFromString:messageId];
this code works.
but when I was debugging, I saw message_id of
NSNumber *message_id = [dictionary valueForKey:#"message_id"]
has a valid value, 21.
Can anyone see the problem here?
You are trying to save a NSString to a NSNumber. If you want it as an NSNumber you can do:
NSNumber *message_id = [NSNumber numberWithInt:[[dictionary valueForKey:#"message_id"] intValue]];
This should solve your problem.
What library are you using to do the conversion? {"message_id":21} means that an NSNumber with a value of 21 should be returned as an NSNumber, {"message_id":"21"} should return it as an NSString.
Using a number formatter is total overkill. Use the method "integerValue" which works just fine both with NSString* and with NSNumber* - you will get the integer 21, whether the object is NSString or NSNumber. The formatter code will obviously run into trouble if your object is an NSNumber and not an NSString.
So: message.messageId = [[NSNumber numberWithInteger:[messageId integerValue]];
I'd probably add a category to NSDictionary
(NSNumber*)nsIntegerNumberForKey:(NSString*)key
which handles the situations where the key is not present, or where the value is a null value or a dictionary or array, so you can use it everywhere you need an NSNumber with an integer value from a JSON document and have error checking everywhere.
Read here SAVING JSON TO CORE DATA and JSON official page
The JSON standard is quite clear about how to distinguish strings from
numbers– basically, strings are surrounded by quotes and numbers are
not. JSON web services however, are not always good about following this requirement. And even when they are, they are not always consistent from one record to another.
So if you have receive NSNumber where NSString is preferred, you must inspect and fix yourself

How to add two NSNumber objects?

Now this must be easy, but how can sum two NSNumber? Is like:
[one floatValue] + [two floatValue]
or exist a better way?
There is not really a better way, but you really should not be doing this if you can avoid it. NSNumber exists as a wrapper to scalar numbers so you can store them in collections and pass them polymorphically with other NSObjects. They are not really used to store numbers in actual math. If you do math on them it is much slower than performing the operation on just the scalars, which is probably why there are no convenience methods for it.
For example:
NSNumber *sum = [NSNumber numberWithFloat:([one floatValue] + [two floatValue])];
Is blowing at a minimum 21 instructions on message dispatches, and however much code the methods take to unbox the and rebox the values (probably a few hundred) to do 1 instruction worth of math.
So if you need to store numbers in dicts use an NSNumber, if you need to pass something that might be a number or string into a function use an NSNumber, but if you just want to do math stick with scalar C types.
NSDecimalNumber (subclass of NSNumber) has all the goodies you are looking for:
– decimalNumberByAdding:
– decimalNumberBySubtracting:
– decimalNumberByMultiplyingBy:
– decimalNumberByDividingBy:
– decimalNumberByRaisingToPower:
...
If computing performance is of interest, then convert to C++ array std::vector or like.
Now I never use C-Arrays anymore; it is too easy to crash using a wrong index or pointer. And very tedious to pair every new [] with delete[].
You can use
NSNumber *sum = #([first integerValue] + [second integerValue]);
Edit:
As observed by ohho, this example is for adding up two NSNumber instances that hold integer values. If you want to add up two NSNumber's that hold floating-point values, you should do the following:
NSNumber *sum = #([first floatValue] + [second floatValue]);
The current top-voted answer is going to lead to hard-to-diagnose bugs and loss of precision due to the use of floats. If you're doing number operations on NSNumber values, you should convert to NSDecimalNumber first and perform operations with those objects instead.
From the 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.
Therefore, you should convert your NSNumber instances to NSDecimalNumbers by way of [NSNumber decimalValue], perform whatever arithmetic you want to, then assign back to an NSNumber when you're done.
In Objective-C:
NSDecimalNumber *a = [NSDecimalNumber decimalNumberWithDecimal:one.decimalValue]
NSDecimalNumber *b = [NSDecimalNumber decimalNumberWithDecimal:two.decimalValue]
NSNumber *result = [a decimalNumberByAdding:b]
In Swift 3:
let a = NSDecimalNumber(decimal: one.decimalValue)
let b = NSDecimalNumber(decimal: two.decimalValue)
let result: NSNumber = a.adding(b)
Why not use NSxEpression?
NSNumber *x = #(4.5), *y = #(-2);
NSExpression *ex = [NSExpression expressionWithFormat:#"(%# + %#)", x, y];
NSNumber *result = [ex expressionValueWithObject:nil context:nil];
NSLog(#"%#",result); // will print out "2.5"
You can also build an NSExpression that can be reused to evaluate with different arguments, like this:
NSExpression *expr = [NSExpression expressionWithFormat: #"(X+Y)"];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:x, #"X", y, #"Y", nil];
NSLog(#"%#", [expr expressionValueWithObject:parameters context:nil]);
For instance, we can loop evaluating the same parsed expression, each time with a different "Y" value:
for (float f=20; f<30; f+=2.0) {
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:x, #"X", #(f), #"Y", nil];
NSLog(#"%#", [expr expressionValueWithObject:parameters context:nil]);
}
In Swift you can get this functionality by using the Bolt_Swift library https://github.com/williamFalcon/Bolt_Swift.
Example:
var num1 = NSNumber(integer: 20)
var num2 = NSNumber(integer: 25)
print(num1+num2) //prints 45