Получение NSDecimalNumber из определенной в локали строки?
У меня есть строка s, которая зависит от локали (например, 0,01 или 0,01). Я хочу преобразовать эту строку в NSDecimalNumber. Из примеров, которые я видел до сих пор в средах , это достигается с помощью NSNumberFormatter a la:
NSString *s = @"0.07"; NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [formatter setGeneratesDecimalNumbers:YES]; NSDecimalNumber *decimalNumber = [formatter numberFromString:s]; NSLog([decimalNumber stringValue]); // prints 0.07000000000000001
Я использую режим 10.4 (в дополнение к тому, чтобы быть рекомендованным в документации, это также единственный режим, ansible на iPhone), но указывающий на форматтер, который я хочу создать десятичные числа. Обратите внимание, что я упростил свой пример (я действительно имею дело с валютными строками). Однако я, очевидно, что-то не так, чтобы вернуть значение, которое иллюстрирует неточность чисел с плавающей запятой.
Каков правильный метод преобразования определенной строки строки в NSDecimalNumber?
- NSLocale currentLocale всегда возвращает «en_US», а не текущий язык пользователя
- Должен ли я использовать свойства или прямую ссылку при доступе к переменным экземпляра внутри?
- Почему переменная NSInteger должна быть отброшена дольше при использовании в качестве аргумента формата?
- Обратный текст NSString
- Глобальная переменная int c
Изменить: Обратите внимание, что мой пример для простоты. Вопрос, который я задаю, также должен касаться того, когда вам нужно взять определенную локальную валютную строку и преобразовать ее в NSDecimalNumber. Кроме того, это можно развернуть до определенной процентной строки в локали и преобразовать ее в NSDecimalNumber.
- Исключение SQLite: SQLite Занято
- Вызов метода экземпляра из метода classа
- Использовать C ++ с Cocoa вместо Objective-C?
- Каков наилучший способ поместить c-struct в NSArray?
- Как называется эта форма определения?
- Префикс имен свойств с подчеркиванием в Objective C
- TouchJSON, работающий с NSNull
- "F" после числа / float в Objective-C / C
Годы спустя:
+(NSDecimalNumber *)decimalNumberWithString:(NSString *)numericString
в NSDecimalNumber
.
Основываясь на ответе Boaz Stuller, я зарегистрировал ошибку для Apple по этой проблеме. Пока это не будет разрешено, вот обходные решения, которые я решил использовать как лучший подход. Эти обходные пути просто полагаются на округление десятичного числа до соответствующей точности, что является простым подходом, который может дополнять ваш существующий код (а не переключиться с форматирования на сканеры).
Общие номера
По сути, я просто округлю число, основанное на правилах, которые имеют смысл для моей ситуации. Таким образом, YMMV зависит от точности, которую вы поддерживаете.
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [formatter setGeneratesDecimalNumbers:TRUE]; NSString *s = @"0.07"; // Create your desired rounding behavior that is appropriate for your situation NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:2 raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE]; NSDecimalNumber *decimalNumber = [formatter numberFromString:s]; NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior]; NSLog([decimalNumber stringValue]); // prints 0.07000000000000001 NSLog([roundedDecimalNumber stringValue]); // prints 0.07
валюты
Обработка валют (это реальная проблема, которую я пытаюсь решить) – это всего лишь небольшая вариация при обработке общих чисел. Ключ состоит в том, что масштаб поведения округления определяется максимальными дробными цифрами, используемыми валютой локали.
NSNumberFormatter *currencyFormatter = [[NSNumberFormatter alloc] init]; [currencyFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; [currencyFormatter setGeneratesDecimalNumbers:TRUE]; [currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle]; // Here is the key: use the maximum fractional digits of the currency as the scale int currencyScale = [currencyFormatter maximumFractionDigits]; NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:currencyScale raiseOnExactness:FALSE raiseOnOverflow:TRUE raiseOnUnderflow:TRUE raiseOnDivideByZero:TRUE]; // image s is some locale specific currency string (eg, $0.07 or €0.07) NSDecimalNumber *decimalNumber = (NSDecimalNumber*)[currencyFormatter numberFromString:s]; NSDecimalNumber *roundedDecimalNumber = [decimalNumber decimalNumberByRoundingAccordingToBehavior:roundingBehavior]; NSLog([decimalNumber stringValue]); // prints 0.07000000000000001 NSLog([roundedDecimalNumber stringValue]); // prints 0.07
Это похоже на работу:
NSString *s = @"0.07"; NSScanner* scanner = [NSScanner localizedScannerWithString:s]; NSDecimal decimal; [scanner scanDecimal:&decimal]; NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithDecimal:decimal]; NSLog([decimalNumber stringValue]); // prints 0.07
Также сообщите об ошибке. Это определенно не правильное поведение, которое вы видите там.
Изменить: пока Apple не исправляет это (а затем каждый потенциальный пользователь обновляется до фиксированной версии OSX), вам, вероятно, придется сворачивать свой собственный парсер с помощью NSScanner или принимать двойную точность «только» для введенных номеров. Если вы планируете иметь бюджет Пентагона в этом приложении, я бы предложил его. Реально, удваивается с точностью до 14 знаков после запятой, поэтому на сумму менее триллиона долларов они будут меньше, чем копейки. Мне пришлось написать свои собственные процедуры синтаксического анализа на основе NSDateFormatter для проекта, и я потратил буквально месяц на обработку всех смешных крайних случаев (например, как только Швеция имеет день недели, включенный в ее длинную дату).
См. Также Лучший способ хранения значений валюты в C ++
Лучшим способом обработки валюты является использование целочисленного значения для наименьшей единицы валюты, т. Е. Центов за доллары / евро и т. Д. Вы избежите ошибок точности, связанных с плавающей точкой, в вашем коде.
Имея это в виду, лучший способ проанализировать строки, содержащие значение валюты, – это сделать это вручную (с настраиваемым символом десятичной точки). Разделите строку в десятичной точке и проанализируйте как первую, так и вторую часть как целочисленные значения. Затем используйте построение своего комбинированного значения.