Mathematical integrity of NSDecimalNumber

好久不见. 提交于 2019-12-20 04:29:17

问题


I'm using numbers divided by 10^30

I may be adding values like 1000000000000000 and 5000000000000000 stored in NSDecimalNumbers.

My concern is that I think I've seen a few times, when adding or subtracting these values, incorrect math being done.

Is that a possibility or are NSDecimalNumbers pretty sound in terms of the integrity of their math.


回答1:


In answer to your question, the math offered by Decimal/NSDecimalNumber is sound, and the problem probably rests in either:

  1. The calculations might exceed the capacity of these decimal formats (as outlined by rob mayoff). For example, this works because we're within the 38 digit mantissa:

    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

    But this will not:

    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

  2. Or, it could just be how you are instantiating these decimal values, e.g. supplying a floating point number rather than using the Decimal(sign:exponent:significand:) or NSDecimalNumber(mantissa:exponent:isNegative:) initializers:

    For example, this works fine:

    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    
    let x = Decimal(sign: .plus, exponent: 30, significand: 1)
    print(formatter.string(for: x)!)
    

    That results in:

    1,000,000,000,000,000,000,000,000,000,000

    But these won't, because you're supplying a floating point number which suffers lower limits in precision:

    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)!)
    

    These both result in:

    1,000,000,000,000,000,409,600,000,000,000

For more information on floating-point arithmetic (and why certainly decimal numbers cannot be perfectly captured in floating-point types), see floating-point arithmetic.


In your other question, you ask why the following:

let foo = NSDecimalNumber(value: 334.99999).multiplying(byPowerOf10: 30)

produced:

334999990000000051200000000000000

This is the same underlying issue that I outlined above in point 2. Floating point numbers cannot accurately represent certain decimal values.

Note, your question is the same as the following Decimal rendition:

let adjustment = Decimal(sign: .plus, exponent: 30, significand: 1)
let foo = Decimal(334.99999) * adjustment

This also produces:

334999990000000051200000000000000

But you will get the desired result if you supply either a string or a exponent and mantissa/significant, because these will be accurately represented as a Decimal/NSDecimalNumber:

let bar = Decimal(string: "334.99999")! * adjustment
let baz = Decimal(sign: .plus, exponent: -5, significand: 33499999) * adjustment

Those both produce:

334999990000000000000000000000000

Bottom line, do not supply floating point numbers to Decimal or NSDecimalNumber. Use string representations or use the exponent and mantissa/significand representation and you will not see these strange deviations introduced when using floating point numbers.




回答2:


I'm using numbers divided by 1^30

Good news, then, because 1^30 = 1. Perhaps you meant 10^30?

Anyway, according to the NSDecimalNumber class reference:

An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.



来源:https://stackoverflow.com/questions/48308639/mathematical-integrity-of-nsdecimalnumber

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!