2015-06-04 4 views
0

Я создаю приложение, которое имеет дело с деньгами, и до сих пор я использую арифметику с плавающей запятой, но я узнал, что лучше использовать NSDecimalNumber.NSDecimalNumber для финансов

Я хочу, чтобы убедиться, что я правильно понял, так что здесь идет:

Представьте себе некоторый рабочий, зарабатывая 20.57 $/час. Эта информация предоставляется пользователем. Я сделал это, как это раньше:

@property (nonatomic) float hourlyRate; 

NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; 
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; 
NSNumber *hourlyRate = [numberFormatter numberFromString:self.rateTextField.text]; 
settingsObject.hourlyRate = [hourlyRate floatValue]; 

Но Я теперь изменил его:

@property (nonatomic) NSDecimalNumber *hourlyRate; 

settingsObject.hourlyRate = (NSDecimalNumber *)[NSDecimalNumber decimalNumberWithString:self.rateTextField.text locale:[NSLocale currentLocale]]; 

Это правильный способ чтения NSDecimalNumbers из строки?

Скажите, что этот человек входит на рабочее место в 10:01. Я сохранить эту информацию следующим образом:

[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:@"start"]; 

После того, как человек закончил, время начала читать NSUserDefaults так:

NSDate *start = [[NSUserDefaults standardUserDefaults] objectForKey:@"start"]; 

Продолжительность рассчитывается следующим образом:

NSTimeInterval interval = [[NSDate date] timeIntervalSinceDate:start]/3600; 

NSDecimalNumber *earned = [settingsObject.hourlyRate decimalNumberByMultiplyingBy:[[NSDecimalNumber alloc] initWithFloat:interval]]; 

Это правильный и эффективный способ, сохраняя точность?

Спасибо!

+0

Не используйте поплавок, используйте двойные. float имеет шестую десятичную точность, double имеет 15. float дает измеримые ошибки в годовой оклад. – gnasher729

+0

@ gnasher729 я вижу. Насколько хорошо это решение работает против ответа Райнера Шварца? Каково ваше мнение по этому поводу? – Erik

ответ

0

Правильно ли это читать NSDecimalNumbers из строки?

Да. Однако вам не нужно указывать результат. Так что нужно просто выглядеть следующим образом:

settingsObject.hourlyRate = [NSDecimalNumber decimalNumberWithString:self.rateTextField.text locale:[NSLocale currentLocale]]; 

И NSLocale часть не является необходимым, если вы не используете внеоборотного локаль:

settingsObject.hourlyRate = [NSDecimalNumber decimalNumberWithString:self.rateTextField.text]; 

Я не думаю, что эта функция любит нильполугруппы значения, так что вы хотели бы, чтобы убедиться, что rateTextField имеет значение не-ноль первый:

if ([self.rateTextField hasText]) { 
    settingsObject.hourlyRate = [NSDecimalNumber decimalNumberWithString:self.rateTextField.text]; 
} 
else { 
    settingsObject.hourlyRate = [NSDecimalNumber zero]; 
} 

является ли это корр ect и наиболее эффективный способ, сохраняя точность?

Да, ваше преобразование интервала в NSDecimal число, а затем умножение выглядит хорошо для меня.

+0

кажется, что это лучший способ обработки денежных расчетов. Возьмите, например, рабочий часовый трекер, действительно ли нужно использовать NSDecimalNumber или удвоить его? Посмотрите на ответ Райнера Шварце, пожалуйста. – Erik

+0

Двойной работает отлично, когда точность не так важна. Но я использую его в любое время, когда я работаю с деньгами.Это не сложнее использовать, чем плавающие значения, и я думаю, что код яснее, чем пытаться запомнить везде, где вам нужно умножить или делить на 100 с целыми числами. –

+0

Да. Итак, вы бы предложили использовать double для приложения отслеживания работы? Выполнение арифметики на NSDecimalNumbers невероятно безнадежное imo, вы должны делать как numberOne = [numberOne decimalNumberByAdding: numberTwo]; вместо просто numberOne + = numberTwo; что, действительно, делает код LOT труднее поддерживать, читать и работать. – Erik

0

Прежде всего, необходимо ответить на этот вопрос:

Насколько точно вы хотите быть - или вы должны быть?

Будет ли ваше приложение создавать счета-фактуры, или это послужит основой для счетов-фактур? Если да, вам, возможно, придется держаться подальше от чисел с плавающей запятой.Возможно, вам также придется подумать о том, как продолжительность фактически используется в расчетах. Могут существовать правила для определенных бизнес-областей или профессий, которые определяют, как измерять/время счета.

Не совсем точно:

Если вам не нужно быть очень точным, я бы продолжать использовать плавающие числа, пока вы не попали препятствия.

Точная:

1. Деньги:

магазин деньги в виде целых чисел, как центов.

2. Время:

Определение временных интервалов и хранить их в виде целых чисел (возможно минут).

3. Расчеты:

Вычислить, используя целые числа. Никогда не используйте поплавки.


Детали:

Если у вас есть вход как 25.07 (я использовал другой номер, чем ваш 20.57 объяснить центов рендеринга) хранить его как 2507. Сделайте свои расчеты в центах. Для отображения конвертируйте в $ или $/hrs. Например:

int amount = 2507; 
int amountDollars = amount/100; 
int amountCents = amount % 100; 

NSString *output = [NSString stringWithFormat:@"%d.%02d $/hrs", amountDollars, amountCents]; 
NSLog(@"output = %@", output); 
// prints: 
// output = 25.07 $/hrs 

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

Предположим, вы сделали счет в единицах 10 минут. Вы, вероятно, затем выставите счет в течение следующих 10 минут, когда они начнутся. Итак, если в записи осталось 11 минут, вам нужно вычислить 20 минут. Аналогичные вопросы для минут - предположим следующие значения:

Actual Time | "Integer Time" 
10:09:59 | 10:09 
10:10:01 | 10:10 
Difference: 
00:00:02 | 00:01 

Если вы просто использовать фактическое время использования плавающих точек первое различие составляет почти ничего - второй суммы на одну минуту. Если ваше время указано в отчете как 10:09 и 10:10, люди задаются вопросом, почему в первом случае сумма почти ничего.

Выполнение финансовых и временных расчетов тяжело, если вам нужно быть действительно точным. Таким образом, это сводится к следующему: сначала узнайте, насколько точно вы должны быть. (Обратите внимание, что «точные» могут иметь странные значения и последствия при входе в правила, законы и т. Д.)

+0

Очень подробный ответ! Благодаря! Хранение как центов, а точнее 100x, что пользователь предоставляет для почасовой ставки, является хорошей идеей, я буду реализовывать это и избавиться от поплавков. Я написал пользовательский объект для хранения значений времени, которые также реализуют пользовательский метод getter и setter, который автоматически пропускает секунды. Поэтому я имею дело с целыми минутами в данный момент. Я только сохраняю время и вычисляю валюту во время выполнения, когда это необходимо. Единственная финансовая ценность, которую я храню, - это почасовая ставка. Пользователь может использовать информацию для счетов-фактур, но в основном предназначен для отслеживания работы. – Erik

+0

Можем ли мы переехать в чат? – Erik

+0

@Erik Sure - не могли бы вы позаботиться о начале чата? –