NSNumber/NSDecimalNumber奇怪的行为 [英] NSNumber/NSDecimalNumber bizarre behavior
问题描述
以下代码为我刚选择的一个测试用例产生了奇怪的结果.操场上的代码,然后是结果
Following code produces bizarre result for a test case I just happened to choose. Code from playground, followed by result
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
-825551 -32 1019123.0799999997952 Minus Eight Hundred Twenty-Five Thousand Five Hundred Fifty-One and -32/100
推荐答案
最重要的是,问题在于您使用Double
调用了spellDollars
,并将其转换为Decimal
.您可以这样做:
Bottom line, the problem is that you called spellDollars
with a Double
, which it converted to a Decimal
. You could do:
print(spellDollars(Decimal(string: "1019123.08")!))
或
print(spellDollars(Decimal(sign: .plus, exponent: -2, significand: 101912308)))
而这两个方法都可以在不将Double
引入Decimal
转换噪声的情况下工作.
And both of those would have worked without introducing this Double
to Decimal
conversion noise.
考虑一个简化的示例:
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
1019123.0799999997952
1019123.08
1019123.08
要了解为什么第一个示例中的Double
转换为Decimal
值1019123.0799999997952
的原因,建议您参考
To understand why that Double
in the first example gets converted to a Decimal
value of 1019123.0799999997952
, I’d suggest you refer to What Every Computer Scientist Should Know About Floating-Point Arithmetic.
关于为什么从intValue
中获得-825551的原因,我想这是由于有效位数(10191230799999997952)太大导致无法以整数格式或类似形式表示而导致的一些溢出.
Regarding why you’re getting -825551 from intValue
, I’d guess it’s some overflow resulting from a significand (10191230799999997952) that is so large that it can’t be represented in an integer format or something like that.
我建议您切开Gordian结,而不要使用NSDecimalNumber
算术来手动舍入值.使用 NSDecimalRound
函数,例如
I’d suggest cutting the Gordian knot and not use NSDecimalNumber
arithmetic to manually round values. Use the NSDecimalRound
function, e.g.
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"
}
这篇关于NSNumber/NSDecimalNumber奇怪的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!