Странность умножения NSDecimalNumber

Некоторое время назад я видел подобную тему об этом, но, похоже, это было связано с тем, что уравнение использовало NSNumber где-то. Я проверил весь свой код, и все, что я вижу, это то, что я использую NSDecimalNumbers для своих уравнений.

У меня есть веб-сервис JSON, который возвращает некоторые номера цен.

Сразу после разбора их в свойствах объекта я хочу запустить с ними следующее уравнение:

Чистая цена * ( 1 + (RawPercentage / 100)) + Фиксированная цена = обработанная чистая цена

Обновление: я изменил код на это:

    NSDecimalNumber *one = [NSDecimalNumber one];

    if (!self.netPrice || [self.netPrice isKindOfClass:[NSNull class]]) 
    { self.netPrice = [NSDecimalNumber zero]; }

    NSDecimalNumber *nettoPrijs = self.netPrice;
    NSLog(@"self.netPrice = %@",self.netPrice);
    //Returns: self.netPrice = 89.25

    NSLog(@"nettoPrijs = %@",nettoPrijs);
    //Returns: nettoPrijs = 89.25

    if (!self.userData.BandFixed || [self.userData.BandFixed isKindOfClass:[NSNull class]]) 
    { self.userData.BandFixed = [NSDecimalNumber zero]; }

    NSDecimalNumber *fixedPrice = self.userData.BandFixed;

    NSLog(@"self.userData.BandFixed = %@",self.userData.BandFixed);
    //Returns: self.userData.BandFixed = 0

    NSLog(@"fixedPrice = %@",fixedPrice);
    //Returns: fixedPrice = 0

    if (!self.userData.BandPercentage || [self.userData.BandPercentage isKindOfClass:[NSNull class]]) 
    { self.userData.BandPercentage = [NSDecimalNumber zero]; }

    NSDecimalNumber *rawPercentage = self.userData.BandPercentage;

    NSLog(@"self.userData.BandPercentage = %@",self.userData.BandPercentage);
    //Returns: self.userData.BandPercentage = 0

    NSLog(@"rawPercentage = %@",rawPercentage);
    //Returns: rawPercentage = 0

    NSDecimalNumber *percentage = [rawPercentage decimalNumberByMultiplyingByPowerOf10:-2];
    NSLog(@"percentage = %@",percentage);
    //Returns: percentage = 0

    NSDecimalNumber *onePercent = [one decimalNumberByAdding:percentage];
    NSLog(@"onePercent = %@",onePercent);
    //Returns: onePercent = 1

    NSDecimalNumber *onePercentTimesNetPrice = [onePercent decimalNumberByMultiplyingBy:nettoPrijs];
    NSLog(@"onePercentTimesNetPrice = %@",onePercentTimesNetPrice);
    //Returns: onePercentTimesNetPrice =        
    -0.000000000000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000359498933794611654993903616

    NSDecimalNumber *addingFixed = [onePercentTimesNetPrice decimalNumberByAdding:fixedPrice];
    NSLog(@"addingFixed = %@",addingFixed);
    //Returns: addingFixed =  
    -0.000000000000000000000000000000000000000000000000000000000000000000000
    00000000000000000000000000359498933794611654993903616

Если я изменю:

NSDecimalNumber *onePercentTimesNetPrice = [onePercent decimalNumberByMultiplyingBy:nettoPrijs];

to:

NSDecimalNumber *onePercentTimesNetPrice = [nettoPrijs decimalNumberByMultiplyingBy:onePercent];

он падает:

-[__NSCFNumber decimalNumberByMultiplyingBy:]: unrecognized selector sent to instance 0x84ad160

person blaa    schedule 10.12.2012    source источник
comment
Постарайтесь сфокусировать свой вопрос. Для меня это слишком долго, чтобы собраться с мыслями в течение нескольких минут, которые у меня есть во время работы.   -  person dasdom    schedule 10.12.2012
comment
Отредактировал это для вас. Надеюсь, это более целенаправленно :)   -  person blaa    schedule 10.12.2012
comment
В чем именно заключается ваш вопрос? Я не понимаю, что вы имеете в виду.   -  person jimpic    schedule 10.12.2012
comment
Каким образом: 89,25 * (1+ ((нуль) / 100)) + (нуль) = -0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000133763189343321587712?   -  person blaa    schedule 10.12.2012
comment
Почему вам нужно использовать NSDecimalNumber в первую очередь? Они боль в тылу, чтобы использовать.   -  person trojanfoe    schedule 10.12.2012
comment
Расскажи мне об этом! Есть ли лучшая альтернатива? Я прочитал в CIMGF, что вы всегда должны использовать NSDecimalNumbers для вещей, связанных с деньгами. Я совсем новичок в Obj-C, поэтому могу ошибаться :).   -  person blaa    schedule 10.12.2012
comment
Что не так с float или double? Они простые, быстрые и работают как положено.   -  person trojanfoe    schedule 10.12.2012
comment
Ну, из-за this : если вы вообще имеете дело с валютой, вам следует использовать NSDecimalNumber. и это: если вам действительно нужно, чтобы ваши результаты точно совпадали, особенно когда вы работаете с деньги: используйте специальный десятичный тип данных.   -  person blaa    schedule 10.12.2012


Ответы (2)


Как показывает ваш вывод NSLog(), self.userData.RawPercentage и self.userData.FixedPrice являются nil, т.е. не выделены + инициализированы как объекты NSDecimalNumber.

nil нельзя использовать для представления десятичного числа ноль!

На самом деле, когда я пробую ваш код,

NSDecimalNumber *custPrice1 = [oneWithNet decimalNumberByAdding:priceFixed];

вылетает, потому что priceFixed равно нулю.

Поэтому вам следует проверить код, в котором инициализируются эти переменные, и убедиться, что они указывают на допустимые объекты NSDecimalNumber.


ОБНОВЛЕНИЕ: сообщение об ошибке

-[__NSCFNumber decimalNumberByMultiplyingBy:]: unrecognized selector sent to instance 0x84ad160

показывает, что nettoPrijs является объектом NSNumber, а не NSDecimalNumber. Следующий упрощенный код показывает, что произойдет, если вы попытаетесь умножить NSDecimalNumber на NSNumber:

NSDecimalNumber *one = [NSDecimalNumber one];
id price = [NSNumber numberWithFloat:89.25];
NSDecimalNumber *product = [one decimalNumberByMultiplyingBy:price];
NSLog(@"product = %@", product);
// Output:
// product = -0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000221166721562906606351581184

(Я понятия не имею, почему это не приводит к сбою или не выдает какое-либо сообщение об ошибке.) Это работает правильно, если вы умножаете на NSDecimalNumber:

price = [NSDecimalNumber decimalNumberWithString:@"89.25"];
product = [one decimalNumberByMultiplyingBy:price];
NSLog(@"product = %@", product);
// Output:
// product = 89.25

Итак, я предполагаю, что self.netPrice — это объект NSNumber, который нужно преобразовать в NSDecimalNumber. То же самое может относиться и к другим числовым объектам.

person Martin R    schedule 10.12.2012
comment
Я использую блоки if (!property || property == NULL || [property isKindOfClass:[NSNull class]]) { property = [NSDecimalNumber zero]; } перед всеми, чтобы проверить, возвращает ли он nil, а затем делаю его [NSDecimalNumber zero]. Надеюсь, это поможет. - person blaa; 10.12.2012
comment
@VincentVeldkamp !property и property == NULL - это один и тот же тест. - person trojanfoe; 10.12.2012
comment
В этом случае я не могу воспроизвести проблему, мой вывод custPrice: 89.25. - person Martin R; 10.12.2012
comment
Я еще раз проверю свой код и попытаюсь проверить, где он идет не так. Исправлю двойной тест. Спасибо! - person blaa; 10.12.2012

Я исправил это, используя:

- (void) doMath
{
    NSDecimalNumber *one = [NSDecimalNumber one];
    NSLog(@"Band: %@ one = %@",self,one);
    if (!self.netPrice || [self.netPrice isKindOfClass:[NSNull class]]) { self.netPrice = [NSDecimalNumber zero]; }
    NSDecimalNumber *nettoPrijs = self.netPrice;
    NSLog(@"self.netPrice = %@",self.netPrice);
    NSLog(@"nettoPrijs = %@",nettoPrijs);
    if (!self.userData.BandFixed || [self.userData.BandFixed isKindOfClass:[NSNull class]]) { self.userData.BandFixed = [NSDecimalNumber zero]; }
    NSDecimalNumber *fixedPrice = self.userData.BandFixed;
    NSLog(@"self.userData.BandFixed = %@",self.userData.BandFixed);
    NSLog(@"fixedPrice = %@",fixedPrice);
    if (!self.userData.BandPercentage || [self.userData.BandPercentage isKindOfClass:[NSNull class]]) { self.userData.BandPercentage = [NSDecimalNumber zero]; }
    NSDecimalNumber *rawPercentage = self.userData.BandPercentage;
    NSLog(@"self.userData.BandPercentage = %@",self.userData.BandPercentage);
    NSLog(@"RawPercentage = %@",rawPercentage);
    NSDecimalNumber *percentage = [rawPercentage decimalNumberByMultiplyingByPowerOf10:-2];
    NSLog(@"percentage = %@",percentage);
    NSDecimalNumber *onePercent = [one decimalNumberByAdding:percentage];
    NSLog(@"onePercent = %@",onePercent);

NSDecimalNumber gertje = [NSDecimalNumber decimalNumberWithDecimal:nettoPrijs.decimalValue]; * NSDecimalNumber *onePercentTimesNetPrice = [gertje decimalNumberByMultiplyingBy:onePercent];

    NSLog(@"onePercentTimesNetPrice = %@",onePercentTimesNetPrice);
    NSDecimalNumber *addingFixed = [onePercentTimesNetPrice decimalNumberByAdding:fixedPrice];
    NSLog(@"addingFixed = %@",addingFixed);
}

Спасибо за вашу помощь!

person blaa    schedule 11.12.2012