skip to Main Content

enter image description here

Why NSDecimalNumber(string:"2175613.285964774433431797660").intValue = 0

NSDecimalNumber(string:"2175613.285964774433431797660").int32Value = 2175613

NSDecimalNumber(string:"2175613.285964774433431797660").int64Value = 0

Who can help me?

Apple M1
Mac OS Ventura 13.0
Xcode 14.1

let a = NSDecimalNumber(string: "2175613.285964774433431797660188672").intValue
// a == 0 is true 

This problem caused our users’ wallet balances to clear directly to zero.

2

Answers


  1. Looks like whenever the decimal significand overflows 64-bit Integer the result is unpredictable regardless of the decimal exponent value. One option to solve your issue is to round your decimal number before converting it to integer. You can overcome this issue as follow:


    extension Decimal {
        func rounded(_ roundingMode: NSDecimalNumber.RoundingMode = .plain) -> Decimal {
            var result = Decimal()
            var number = self
            NSDecimalRound(&result, &number, 0, roundingMode)
            return result
        }
    
        var whole: Decimal { rounded(sign == .minus ? .up : .down) }
        var fraction: Decimal { self - whole }
    }
    

    extension Decimal {
        var number: NSDecimalNumber { self as NSDecimalNumber }
    
        var int: Int? {
            let whole = self.whole
            guard whole > Decimal(Int.min) && whole <= Decimal(Int.max) else { return nil }
            return whole.number.intValue
        }
    
        var int32: Int32? {
            let whole = self.whole
            guard whole > Decimal(Int32.min) && whole <= Decimal(Int32.max) else { return nil }
            return whole.number.int32Value
        }
    
        var int64: Int64? {
            let whole = self.whole
            guard whole > Decimal(Int64.min) && whole <= Decimal(Int64.max) else { return nil }
            return whole.number.int64Value
        }
    }
    


    let decimal1 = Decimal(string: "9223372036854775807")!
    decimal1.int    // 9,223,372,036,854,775,807
    decimal1.int32  // nil overflows Int32.max
    decimal1.int64  // 9,223,372,036,854,775,807
    

    let decimal2 = Decimal(string: "9223372036854775808")! // overflows Int.max
    decimal2.significand        //  9,223,372,036,854,775,808 overflows Int.max
    decimal2.number.intValue    // -9,223,372,036,854,775,808 undefined behavior
    decimal2.number.int64Value  // -9,223,372,036,854,775,808 undefined behavior
    decimal2.int    // nil
    decimal2.int32  // nil
    decimal2.int64  // nil
    

    let decimal3 = Decimal(string: "2175613.9223372036854775807")! // overflows Int.max
    String(describing: decimal3.significand).count // 26
    decimal3.exponent           // -19
    decimal3.significand        //  21,756,139,223,372,036,854,775,807 overflows Int.max
    decimal3.number.intValue    // 0 undefined behavior
    decimal3.number.int64Value  // 0 undefined behavior
    decimal3.int    // 2,175,613
    decimal3.int32  // 2,175,613
    decimal3.int64  // 2,175,613
    
    Login or Signup to reply.
  2. It looks like you’re having issues with converting strings to decimals.

    Keep in mind that users may have different device locales, so an incorrect decimal separator could result in the wrong output.

    // Your example is using "default" locale, which has variable separator
    NSDecimalNumber(string:"2175613.9223372036854775807").intValue                      // 0
    NSDecimalNumber(string:"2175613.9223372036854775807").int8Value                     // 125
    NSDecimalNumber(string:"2175613.9223372036854775807").int16Value                    // 12 925
    NSDecimalNumber(string:"2175613.9223372036854775807").int32Value                    // 2 175 613
    NSDecimalNumber(string:"2175613.9223372036854775807").int64Value                    // 0
    
    // Correct separator for the en_US locale is comma – ","
    let enLocale = NSLocale(localeIdentifier: "en_US")
    NSDecimalNumber(string:"2175613,9223372036854775807", locale: enLocale).intValue    // 2 175 613
    NSDecimalNumber(string:"2175613,9223372036854775807", locale: enLocale).int8Value   // 125
    NSDecimalNumber(string:"2175613,9223372036854775807", locale: enLocale).int16Value  // 12 925
    NSDecimalNumber(string:"2175613,9223372036854775807", locale: enLocale).int32Value  // 2 175 613
    NSDecimalNumber(string:"2175613,9223372036854775807", locale: enLocale).int64Value  // 2 175 613
    
    // Correct separator for the ru_UA locale is dot – "."
    let ruLocale = NSLocale(localeIdentifier: "ru_UA")
    NSDecimalNumber(string:"2175613.9223372036854775807", locale: ruLocale).intValue    // 2 175 613
    NSDecimalNumber(string:"2175613.9223372036854775807", locale: ruLocale).int8Value   // 125
    NSDecimalNumber(string:"2175613.9223372036854775807", locale: ruLocale).int16Value  // 12 925
    NSDecimalNumber(string:"2175613.9223372036854775807", locale: ruLocale).int32Value  // 2 175 613
    NSDecimalNumber(string:"2175613.9223372036854775807", locale: ruLocale).int64Value  // 2 175 613
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search