Я имею дело с денежными суммами в моем приложении, поэтому точность, очевидно, очень важна, а десятичное число / NSDecimal должно быть способом быстрой обработки денег. Я хочу конвертировать между десятичным и строковым, сохраняя точность. (парсинг ответов API, сохранение в БД ...)
Это мое текущее решение:
private let dotLocaleDictionary = [NSLocale.Key.decimalSeparator: "."]
func apiStringToDecimal(_ string: String) -> Decimal? {
let number = NSDecimalNumber(string: string, locale: dotLocaleDictionary) as Decimal
return number.isNaN ? nil : number
}
func decimalToApiString(_ decimal: Decimal) -> String {
return (decimal as NSDecimalNumber).description(withLocale: dotLocaleDictionary)
}
Словарь локали заботится о десятичном разделителе, и я ожидал, что инициализатор строки будет точным. Но во время игры на детской площадке я заметил, что после определенной длины входных строк результирующие числа обрезаются.
Я также пытался проанализировать строки с помощью средства форматирования чисел, но результаты были еще хуже.
Код моей детской площадки:
let small = "1.135160000500009000100020003000400050061111"
let big = "1234567890123456789000100020003000400061111"
let medium = "123456789123456789.0001000200030004000511111"
let negative = "-123456789123456789.0001000200030004000511111"
let numericStrings = [small, big, medium, negative]
numericStrings.forEach {
print($0)
guard let decimal = apiStringToDecimal($0) else {
print("Failed to parse decimal from: \($0)")
print("--------------------")
return
}
print(decimal)
let string = decimalToApiString(decimal)
print($0 == string ? "match" : "mismatch")
print("--------------------")
}
let max = Decimal.greatestFiniteMagnitude
print("MAX decimal: \(max)")
print((max as NSDecimalNumber).description(withLocale: dotLocaleDictionary))
print(decimalToApiString(max))
И вывод, который он производит:
1.135160000500009000100020003000400050061111
1.13516000050000900010002000300040005006
mismatch
--------------------
1234567890123456789000100020003000400061111
1234567890123456789000100020003000400060000
mismatch
--------------------
123456789123456789.0001000200030004000511111
123456789123456789.000100020003000400051
mismatch
--------------------
-123456789123456789.0001000200030004000511111
-123456789123456789.000100020003000400051
mismatch
--------------------
MAX decimal: 3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
3402823669209384634633746074317682114550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Преобразование из десятичной в строку, кажется, работает нормально, проблема в строке в десятичную.
Так как мои числа меньше максимального десятичного числа, я не думаю, что мои тестовые числа просто слишком велики.
Я почти уверен, что исключил проблемы с печатью и запустил код на реальном устройстве, так что это не проблема игровой площадки.
Я что-то не так делаю? Есть ли лучший способ конвертировать между десятичным и строковым в Swift?
Или строки просто слишком длинные? (я не нашел никакой документации / обсуждения того, почему это не будет работать)