NSNumber / NSDecimalNumber奇怪的行为

时间:2019-02-10 21:15:45

标签: swift macos

以下代码为我刚选择的一个测试用例产生了奇怪的结果。操场上的代码,然后是结果

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 :(得分:3)

最重要的是,问题在于您使用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.0799999997952
  1019123.08
  1019123.08

要了解为什么第一个示例中的Double会转换为Decimal的{​​{1}}值,建议您参考What Every Computer Scientist Should Know About Floating-Point Arithmetic


关于为什么您从1019123.0799999997952得到-825551的原因,我想这是由于有效位数(10191230799999997952)太大导致无法以整数格式或类似形式表示而导致的一些溢出那。

我建议您切开Gordian结,而不要使用intValue算术来手动舍入值。使用NSDecimalRound函数,例如

NSDecimalNumber