Simplify NSDecimalNumber initialisation - objective-c

I have the following construct, which I would like to simplify. I had to use a NSString (?) in order to get rid of the NSNumber vs. NSDecimalNumber compiler Warning.
NSDecimalNumber *ticksSinceSeventies = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:#"%d",[self timeIntervalSince1970]]];
Thanks for your Help!

If you are only using NSTimeIntervals, which are doubles, then the base 10 arithmetic that NSDecimalNumber affords you isn't really necessary. You can just use NSNumber instead.
Your constructor would therefore just be:
NSNumber *ticksSinceSeventies = [NSNumber numberWithDouble:[self timeIntervalSince1970]];
(assuming self is an NSDate subclass, or this is inside a category method).

Here is one way:
[NSDecimalNumber decimalNumberWithString:[[self timeIntervalSince1970] stringValue]];
There may be ways to simplify it still better, but i am not aware.

Related

how to operate on NSNumber in NSNArray created by NSString

i've got some method.
....{
NSString *mean = #"1:1:1:1:1:1:1:1:1";
tab = [self moveSourceArrayToDestinationArray:mean];
....}
-(NSArray*)moveSourceArrayToDestinationArray:(NSString*)sourceArray{
NSArray *destinationArray = [sourceArray componentsSeparatedByString:#":"];
for (NSNumber *number in destinationArray) {
sum += [number doubleValue];
}
NSLog(#"%d", [destinationArray objectAtIndex:1] * 5); // invalid operands to binary expression ('id' and 'int')
return destinationArray;
}
how can i do something mathematic operation on numbers in NSArray?
Your reference to NSNumber in your code is a mistake, and you got lucky that your code didn't throw an unrecognized selector exception. Your destinationArray is an array of NSStrings, not NSNumbers. It just so happens that both NSString and NSNumber have doubleValue and intValue methods. So when you say [number doubleValue] in your loop, you actually end up calling [NSString doubleValue] which of course still returns the number you want. However, if you were to try to call [number shortValue], where shortValue is a selector that only exists in NSNumber and not NSString, your code would throw an exception and not work.
The moral of this answer is that you should remove any reference to NSNumber in your code, or actually convert the objects in destinationArray to NSNumbers. Otherwise, you risk running into more trouble.
The easiest thing to do is to retrieve a numeric value from the NSNumber object, something you already did in your code snippet earlier. For example, try:
NSLog(#"%d", [[destinationArray objectAtIndex:1] intValue] * 5);
See the full list of numeric access functions in the NSNumber documentation under the section titled "Accessing Numeric Values."
Your object at [destionationArray objectAtIndex:index] is likely to be a NSNumber as well. Therefore, you have to to [[destinationArray objectAtIndex:index] doubleValue]at this point, too.

Sum NSArray of NSDecimalNumbers

I know you can sum an array of NSNumbers by using the #sum.self keypath, but does that also work with NSDecimalNumbers? Will the result be accurate?
EDIT: To be more specific, here is code I know that works with NSNumber.
NSNumber *sum = [numArray valueForKeyPath:#"#sum.self"];
NSDecimalNumber is a subclass of NSNumber, so it will inherit this ability.
Also, I would recommend using one of these:
NSDecimalNumber *sum = [numArray valueForKeyPath:#"#sum.floatValue"];
float sum = [[numArray valueforKeyPath:#"#sum.floatValue"] floatValue];

#"" string type literals for NSNumber

I love the shorthand handling of string literals in Objective C with the #"string" notation. Is there any way to get similar behavior with NSNumbers? I deal with numbers more and it's so tedious having [NSNumber numberWithWhatever:] calls everywhere. Even creating a macro would work, but my knowledge of how best to do that is limited.
Since nobody has mentioned this... If you need to wrap a value in an NSNumber, the NSNumber literal syntax is as follows.
int val = 13;
NSNumber *numVal = #(val);
As of Clang v3.1 you can now use Objective-C literals.
NSNumber *fortyTwo = #42; // equivalent to [NSNumber numberWithInt:42]
NSNumber *fortyTwoUnsigned = #42U; // equivalent to [NSNumber numberWithUnsignedInt:42U]
NSNumber *fortyTwoLong = #42L; // equivalent to [NSNumber numberWithLong:42L]
NSNumber *fortyTwoLongLong = #42LL; // equivalent to [NSNumber numberWithLongLong:42LL]
So, answering your specific question:
[Tyler setArms:[[[NSNumber alloc] initWithInt:1] autorelease]];
Can now be written as:
[Tyler setArms:#1];
There are also literals for arrays and dictionaries, but they are beyond the scope of this question.
To take advantage of literals in Xcode you'll need at least version 4.4 -- this comes with Apple's LLVM 4.0 compiler.
I'm using a macro like
#define N(x) [NSNumber numberWithInt: x]
wich leads to code like
[N(123) intValue];
update:
One should be aware of the CPU and memory consumption of such a macro. While the #"…" strings are static compiler generated strings of the constant string class (depends on foundation maybe NSConstantString in Cocoa?) the macros create code which is evaluated at runtime and therefore create a new object every time they are called.
Xcode 4.4 has introduced the Clang features that rjstelling mentioned for literals for NSNumber, NSArray and NSDictionary. The syntax is simple:
//Number literal
NSNumber *pi = #3.14;
//Array literal
NSArray *primes = #[ #2, #3, #5, #7, #11 ]; //No nil terminator needed
//Dictionary literal
NSDictionary *dict = #{
#"key1": #42,
#"key2": #"Another key",
#3: #"A NSNumber key"
}; //No nil terminator, stored in "key:value," format

Changing value of a NSNumber

Is there a way to change the value contained in an NSNumber after it is created without making it point to a different NSNumber?
NSNumber *num = [NSNumber numberWithInt:0];
num = [NSNumber numberWithInt: 1]; // now num points to a different object, which I don't want. I want it the same object still, but different value.
NSNumber is immutable. Actually, it's a subclass of NSValue, and all NSValues are immutable.
No, you can't change the value of NSNumber.
See, for example, this post.

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