Как добавить два объекта NSNumber?

79

Это должно быть легко, но как можно суммировать два NSNumber? Как:

[one floatValue] + [two floatValue]

или есть лучший способ?

mamcx
источник

Ответы:

143

На самом деле нет лучшего способа, но вам действительно не следует этого делать, если вы можете этого избежать. NSNumberсуществует как оболочка для скалярных чисел, поэтому вы можете хранить их в коллекциях и передавать их полиморфно с другими NSObjects. На самом деле они не используются для хранения чисел в реальной математике. Если вы выполняете математические операции с ними, это будет намного медленнее, чем выполнение операции только со скалярами, поэтому, вероятно, для этого не существует удобных методов.

Например:

NSNumber *sum = [NSNumber numberWithFloat:([one floatValue] + [two floatValue])];

Выполняет как минимум 21 инструкцию по отправке сообщений, и сколько бы кода ни занимали методы, чтобы распаковать и переупаковывать значения (вероятно, несколько сотен) для выполнения математических расчетов на одну инструкцию.

Поэтому, если вам нужно хранить числа в dicts, используйте NSNumber, если вам нужно передать что-то, что может быть числом или строкой, в функцию, используйте NSNumber, но если вы просто хотите выполнять математические вычисления со скалярными типами C.

Луи Гербарг
источник
1
Я бы добавил, что если вам нужно хранить коллекции чисел, для которых вы собираетесь выполнять много математических операций - возможно, вам действительно нужен массив в стиле C правильного числового типа?
Кендалл Хелмштеттер Гельнер,
Когда вы много занимаетесь математикой, вы хотите использовать скаляры, вы хотите использовать объекты, когда вам нужен полиморфизм или они должны уместиться в существующих контейнерах.
Луи Гербарг
Правильный метод для NSNumber - 'numberWithFloat:' - возможно, это изменилось с момента ответа.
Ник
Вы правы, геттером является floatValue, поэтому я обычно набираю его в обоих местах, но stackoverflow не выдает мне ошибки компиляции. Исправлено
Луи Гербарг 02
2
@clopez: «Доктор, мне больно, когда я это делаю». NSNumber - это упакованный тип для использования с контейнерами, а не для арифметики. Если вы занимаетесь арифметикой, используйте арифметический тип.
Стивен Кэнон
49

NSDecimalNumber (подкласс NSNumber ) имеет все полезности, которые вы ищете:

– decimalNumberByAdding:
– decimalNumberBySubtracting:
– decimalNumberByMultiplyingBy:
– decimalNumberByDividingBy:
– decimalNumberByRaisingToPower:

...

Если интересует производительность вычислений, преобразуйте его в массив C ++ std :: vector или что-то подобное.

Теперь я больше не использую C-Arrays; слишком легко выйти из строя, используя неправильный индекс или указатель. И очень утомительно связывать каждый новый [] с delete [].

Земмель
источник
Не могли бы вы уточнить, что вы имеете в виду под «очень утомительно связывать каждый новый [] с delete [].»?
jpswain
Скажем так. В c вообще избегайте использования указателя. Вся суть objective-c заключается в том, чтобы поместить слой поверх C, о котором вам НИКОГДА не нужно беспокоиться. C не предназначен для прямого использования. Это то, чем нужно «управлять» в первую очередь.
user4951
После добавления двух NSDecimalNumbers, как преобразовать NSDecimalNumberрезультат обратно в NSNumber? (Этот вопрос касается сложения двух NSNumber, а не двух NSDecimalNumber.)
ohho
зачем вам вообще использовать массив C, если у вас есть вектор?
Джейк Лонг
1
Почему вы хотите преобразовать NSDecimalNumber обратно в NSNumber? NSDecimalNumber - это NSNumber:@interface NSDecimalNumber : NSNumber
Стивен Фишер,
12

Вы можете использовать

NSNumber *sum = @([first integerValue] + [second integerValue]);

Изменить: по наблюдениям ohho , этот пример предназначен для добавления двух NSNumberэкземпляров, которые содержат целые значения. Если вы хотите сложить два NSNumber, которые содержат значения с плавающей запятой, вы должны сделать следующее:

NSNumber *sum = @([first floatValue] + [second floatValue]);
Немезида
источник
В чем причина символа «@»? Я не уверен, что понимаю, что он делает в этом контексте.
Майк Мейерс
Ах. Теперь я понимаю .... это литерал NSNumber, как описано здесь: stackoverflow.com/a/11120371/35723
Майк Мейерс
Да, @mikemeyers это буквальное , что компрессы примитивы , как intи longили это большой брат typedef« под ред NSIntegerв NSNumber объекты .
nemesis
integerValueусекает float. Как можно проголосовать за этот ответ?
ohho 08
@ohho это был простой пример. Зачем вам использовать, integerValueесли вы хотите сохранить десятичную часть чисел?
nemesis
6

Текущий ответ, получивший наибольшее количество голосов, приведет к трудно диагностируемым ошибкам и потере точности из-за использования поплавков. Если вы выполняете числовые операции со значениями NSNumber, вам следует сначала преобразовать в NSDecimalNumber и вместо этого выполнять операции с этими объектами.

Из документации :

NSDecimalNumber, неизменный подкласс NSNumber, предоставляет объектно-ориентированную оболочку для выполнения арифметических операций с основанием 10. Экземпляр может представлять любое число, которое может быть выражено как мантисса x 10 ^ экспонента, где мантисса - десятичное целое число длиной до 38 цифр, а экспонента - целое число от –128 до 127.

Следовательно, вы должны преобразовать свои экземпляры NSNumber в NSDecimalNumbers путем [NSNumber decimalValue]выполнения любых арифметических действий , которые вы хотите, а затем присвоить обратно NSNumber, когда вы закончите.

В Objective-C:

NSDecimalNumber *a = [NSDecimalNumber decimalNumberWithDecimal:one.decimalValue]
NSDecimalNumber *b = [NSDecimalNumber decimalNumberWithDecimal:two.decimalValue]
NSNumber *result = [a decimalNumberByAdding:b]

В Swift 3:

let a = NSDecimalNumber(decimal: one.decimalValue)
let b = NSDecimalNumber(decimal: two.decimalValue)
let result: NSNumber = a.adding(b)
Дэн Лёвенгерц
источник
0

Почему бы не использовать 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"

Вы также можете создать выражение NSE, которое можно повторно использовать для оценки с разными аргументами, например:

NSExpression *expr = [NSExpression expressionWithFormat: @"(X+Y)"];
NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:x, @"X", y, @"Y", nil];
NSLog(@"%@", [expr expressionValueWithObject:parameters context:nil]);

Например, мы можем в цикле вычислять одно и то же проанализированное выражение, каждый раз с другим значением «Y»:

 for (float f=20; f<30; f+=2.0) {
    NSDictionary *parameters = [NSDictionary dictionaryWithObjectsAndKeys:x, @"X", @(f), @"Y", nil];
    NSLog(@"%@", [expr expressionValueWithObject:parameters context:nil]);
 }
Мотти Шнеор
источник
-1

В Swift вы можете получить эту функциональность с помощью библиотеки Bolt_Swift https://github.com/williamFalcon/Bolt_Swift .

Пример:

  var num1 = NSNumber(integer: 20)
  var num2 = NSNumber(integer: 25)
  print(num1+num2) //prints 45
Уильям Фалькон
источник