NSDecimalNumber 的数学完整性
Posted
技术标签:
【中文标题】NSDecimalNumber 的数学完整性【英文标题】:Mathematical integrity of NSDecimalNumber 【发布时间】:2018-01-17 19:31:08 【问题描述】:我用的是数字除以 10^30
我可能会添加存储在NSDecimalNumber
s 中的值,例如 1000000000000000 和 5000000000000000。
我担心的是,我曾多次看到,在添加或减去这些值时,会出现不正确的数学运算。
这是一种可能性吗,或者NSDecimalNumbers
就他们的数学完整性而言非常合理。
【问题讨论】:
你能举个具体的例子吗? 我现在正在尝试重新创建,我会的 @ZackShapiro 您应该使用 Swift 原生类型,例如Decimal
它比 NSDecimalNumber 除了是原生的 swift 类型还有什么好处?
好处包括(a)价值与参考语义; (b) 消除NS
句法噪音; (c) 内置+
、-
等运算符,代码更自然直观。而且,正如Apple says,“在导入 Foundation 框架时,Swift 覆盖层为许多桥接引用类型提供了值类型。 ... 比起桥接的 Objective-C 引用类型,更喜欢 Swift 值类型”。
【参考方案1】:
在回答您的问题时,Decimal
/NSDecimalNumber
提供的数学是合理的,问题可能在于:
计算可能超出这些十进制格式的容量(如 rob mayoff 所述)。例如,这是因为我们在 38 位尾数之内:
let x = Decimal(sign: .plus, exponent: 60, significand: 1)
let y = Decimal(sign: .plus, exponent: 30, significand: 1)
let z = x + y
1,000,000,000,000,000,000,000,000,000,001,000,000,000,000,000,000,000,000,000,000
但这不会:
let x = Decimal(sign: .plus, exponent: 60, significand: 1)
let y = Decimal(sign: .plus, exponent: 10, significand: 1)
let z = x + y
1,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000
或者,它可能只是您实例化这些十进制值的方式,例如提供浮点数而不是使用 Decimal(sign:exponent:significand:)
或 NSDecimalNumber(mantissa:exponent:isNegative:)
初始化器:
例如,这很好用:
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let x = Decimal(sign: .plus, exponent: 30, significand: 1)
print(formatter.string(for: x)!)
结果:
1,000,000,000,000,000,000,000,000,000,000
但这些不会,因为您提供的浮点数在精度上会受到下限:
let y = Decimal(1.0e30)
print(formatter.string(for: y)!)
let z = Decimal(1_000_000_000_000_000_000_000_000_000_000.0)
print(formatter.string(for: z)!)
这些都导致:
1,000,000,000,000,000,409,600,000,000,000
有关浮点运算的更多信息(以及为什么在浮点类型中不能完美捕获十进制数),请参阅floating-point arithmetic。
在your other question 中,您会问为什么:
let foo = NSDecimalNumber(value: 334.99999).multiplying(byPowerOf10: 30)
制作:
3349999900000000051200000000000000
这与我在上面第 2 点中概述的基本问题相同。浮点数不能准确地表示某些十进制值。
请注意,您的问题与以下Decimal
翻译相同:
let adjustment = Decimal(sign: .plus, exponent: 30, significand: 1)
let foo = Decimal(334.99999) * adjustment
这也会产生:
3349999900000000051200000000000000
但是,如果您提供字符串或指数和尾数/有效值,您将获得所需的结果,因为这些将准确地表示为 Decimal
/NSDecimalNumber
:
let bar = Decimal(string: "334.99999")! * adjustment
let baz = Decimal(sign: .plus, exponent: -5, significand: 33499999) * adjustment
两者都产生:
334999990000000000000000000000000
底线,不要向Decimal
或NSDecimalNumber
提供浮点数。使用字符串表示或使用指数和尾数/有效数表示,您不会看到使用浮点数时引入的这些奇怪的偏差。
【讨论】:
【参考方案2】:我用的是数字除以 1^30
那么好消息,因为 1^30 = 1。也许您的意思是 10^30?
反正照着NSDecimalNumber
class reference:
一个实例可以表示任何可以表示为尾数 x 10^exponent 的数字,其中尾数是最多 38 位的十进制整数,指数是从 –128 到 127 的整数。
【讨论】:
以上是关于NSDecimalNumber 的数学完整性的主要内容,如果未能解决你的问题,请参考以下文章