iOS 中的NSDecimalNumber使用

3,181 阅读2分钟

前言

在开发项目中,我们或多或少都会有金钱方面的计算,如果使用DoubleFloat的话会有精确度问题,因此,苹果提供了NSDecimalNumber这个API,下面我们你就来使用它

1、 NSDecimalNumber

NSDecimalNumber是一种精确计算,主要解决一下问题:

1、字符串转float等不精确问题。
2、精确计算
3、保留小数位数
4、四舍五入及其它的保留小数位数规则

2、 NSDecimalNumber使用

  • StringNSDecimalNumber
let decimalNumber = NSDecimalNumber(string: "0.123")
  • 加法计算
let decimalNumber1 = NSDecimalNumber(string: "321.321")
let decimalNumber2 = NSDecimalNumber(string: "123.123")
let sum = decimalNumber1.adding(decimalNumber2)
debugPrint(sum)

输出:444.444
  • 减法计算
let decimalNumber1 = NSDecimalNumber(string: "321.321")
let decimalNumber2 = NSDecimalNumber(string: "123.123")
let subtracting = decimalNumber1.subtracting(decimalNumber2)
debugPrint(subtracting)

输出:198.198
  • 乘法计算
let decimalNumber1 = NSDecimalNumber(string: "321.321")
let decimalNumber2 = NSDecimalNumber(string: "123.123")
let multiplying = decimalNumber1.multiplying(by: decimalNumber2)
debugPrint(multiplying)

输出:39562.005483
  • 除法计算
let decimalNumber1 = NSDecimalNumber(string: "321.321")
let decimalNumber2 = NSDecimalNumber(string: "123.123")
let dividing = decimalNumber1.dividing(by: decimalNumber2)
debugPrint(dividing)

输出:2.60975609756097560975609756097560975609
  • 每次都这样写的话有点麻烦,我们给NSDecimalNumber添加四个扩展+-*/
public extension NSDecimalNumber {
    static func + (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber {
        return lhs.adding(rhs)
    }

    static func - (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber {
        return lhs.subtracting(rhs)
    }

    static func * (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber {
        return lhs.multiplying(by: rhs)
    }

    static func / (lhs: NSDecimalNumber, rhs: NSDecimalNumber) -> NSDecimalNumber {
        return lhs.dividing(by: rhs)
    }
}

这样直接使用+-*/就可以了

let sum = decimalNumber1 + decimalNumber2
let subtracting = decimalNumber1 - decimalNumber2
let multiplying = decimalNumber1 * decimalNumber2
let dividing = decimalNumber1 / decimalNumber2
  • 如果我们想要保留几位小数点该怎么弄呢?我们可以使用NSDecimalNumberHandler,下面是初始化方法
public init(roundingMode: NSDecimalNumber.RoundingMode, scale: Int16, raiseOnExactness exact: Bool, raiseOnOverflow overflow: Bool, raiseOnUnderflow underflow: Bool, raiseOnDivideByZero divideByZero: Bool)

我们可以看到里面有一个RoundingMode枚举

public enum RoundingMode : UInt {
    case plain = 0
    case down = 1
    case up = 2
    case bankers = 3
}

其中4个值的大致意思是:

plain: 保留位数的下一位四舍五入
down: 保留位数的下一位直接舍去
up: 保留位数的下一位直接进一位
bankers: 当保留位数的下一位不是5时,四舍五入,当保留位数的下一位是5时,其前一位是偶数直接舍去,是奇数直接进位(如果5后面还有数字则直接进位)

我们使用第一个plain来试一下,保留两位小数,其中scale就是精确到几位小数

let decimalNumber1 = NSDecimalNumber(string: "321.321")
let decimalNumber2 = NSDecimalNumber(string: "123.123")
let sum = decimalNumber1.adding(decimalNumber2)

let behavior = NSDecimalNumberHandler(roundingMode: .plain, scale: Int16(2), raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true)

debugPrint(product.stringValue)

输出:444.44
  • 接下来给NSDecimalNumber扩展一个转String的方法
public extension NSDecimalNumber {
    /// 转String 四舍五入
    /// - Parameter scale: 保留几位小数
    /// - Parameter roundingMode:
    ///     plain: 保留位数的下一位四舍五入
    ///     down: 保留位数的下一位直接舍去
    ///     up: 保留位数的下一位直接进一位
    ///     bankers: 当保留位数的下一位不是5时,四舍五入,当保留位数的下一位是5时,其前一位是偶数直接舍去,是奇数直接进位(如果5后面还有数字则直接进位)
    func toString(_ scale: Int = 2,
                  roundingMode: RoundingMode = .plain) -> String {
        let behavior = NSDecimalNumberHandler(
            roundingMode: roundingMode,
            scale: Int16(scale),
            raiseOnExactness: false,
            raiseOnOverflow: false,
            raiseOnUnderflow: false,
            raiseOnDivideByZero: true)
        let product = multiplying(by: .one, withBehavior: behavior)
        return product.stringValue
    }
}