NSNumber / NSDecimalNumber странное поведение - PullRequest
0 голосов
/ 11 февраля 2019

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

import Cocoa

func spellDollars(_ amount: Decimal) -> String {
    let fDollars = NumberFormatter()
    fDollars.numberStyle = NumberFormatter.Style.spellOut
    let fCents = NumberFormatter()
    fCents.format = "00"
    let c = (amount * 100 as NSDecimalNumber).intValue % 100
    let cents = fCents.string(from: NSNumber(value: c))!
    let d  = (amount as NSDecimalNumber).intValue
    print(d, c, amount)
    let dollars = fDollars.string(from: NSNumber(value: d))!.capitalized
    let s = "\(dollars) and \(cents)/100"
    return s
}

print(spellDollars(1019123.08))

Результат:

-825551 -32 1019123.0799999997952 Минус восемьсот двадцать пять тысяч пятьсот пятьдесят один и -32 / 100

1 Ответ

0 голосов
/ 11 февраля 2019

В итоге, проблема в том, что вы назвали spellDollars с Double, который он преобразовал в Decimal.Вы можете сделать:

print(spellDollars(Decimal(string: "1019123.08")!))

Или

print(spellDollars(Decimal(sign: .plus, exponent: -2, significand: 101912308)))

И то и другое сработало бы без введения этого шума преобразования Double в Decimal.


Рассмотрим упрощенный пример:

let decimal1 = Decimal(floatLiteral: 1019123.08)
let decimal2 = Decimal(string: "1019123.08")!
let decimal3 = Decimal(sign: .plus, exponent: -2, significand: 101912308)

for value in [decimal1, decimal2, decimal3] {
    print(value)
}

Будет выведено:

1019123.07999999979521019123,081019123.08

Чтобы понять, почему Double в первом примере преобразуется в Decimal значение 1019123.0799999997952, я бы посоветовал вам обратиться к Что должен знать каждый компьютерный ученыйЧто касается арифметики с плавающей точкойон не может быть представлен в целочисленном формате или что-то в этом роде.

Я бы предложил разрезать узел Гордиана и не использовать арифметику NSDecimalNumber для округления значений вручную.Используйте функцию NSDecimalRound, например,

func spellDollars(_ amount: Decimal) -> String {
    // instantiate formatters

    let dollarsFormatter = NumberFormatter()
    dollarsFormatter.numberStyle = .spellOut
    let centsFormatter = NumberFormatter()
    centsFormatter.minimumIntegerDigits = 2

    // get dollars

    var inputAmount = amount
    var dollars = Decimal.zero
    NSDecimalRound(&dollars, &inputAmount, 0, .down)

    // get dollars and cents

    var dollarsAndCents = Decimal.zero
    NSDecimalRound(&dollarsAndCents, &inputAmount, 2, .plain)

    // get cents

    let cents = (dollarsAndCents - dollars) * 100

    // build strings

    let centsString = centsFormatter.string(for: cents)!
    let dollarsString = dollarsFormatter.string(for: dollars)!.capitalized
    return "\(dollarsString) and \(centsString)/100"
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...