NSDecimalNumber 的数学完整性

Posted

技术标签:

【中文标题】NSDecimalNumber 的数学完整性【英文标题】:Mathematical integrity of NSDecimalNumber 【发布时间】:2018-01-17 19:31:08 【问题描述】:

我用的是数字除以 10^30

我可能会添加存储在NSDecimalNumbers 中的值,例如 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

底线,不要向DecimalNSDecimalNumber 提供浮点数。使用字符串表示或使用指数和尾数/有效数表示,您不会看到使用浮点数时引入的这些奇怪的偏差。

【讨论】:

【参考方案2】:

我用的是数字除以 1^30

那么好消息,因为 1^30 = 1。也许您的意思是 10^30?

反正照着NSDecimalNumber class reference:

一个实例可以表示任何可以表示为尾数 x 10^exponent 的数字,其中尾数是最多 38 位的十进制整数,指数是从 –128 到 127 的整数。

【讨论】:

以上是关于NSDecimalNumber 的数学完整性的主要内容,如果未能解决你的问题,请参考以下文章

我应该使用 NSDecimalNumber 来处理金钱吗?

初二上下的数学公式!我要完整的!

数学科学的完整课程大纲(工科自学必看)

数学科学的完整课程大纲(工科自学必看)

一图看完整个数学脉络

一图看完整个数学脉络