舍入 NSDecimalNumber HalfDown

Posted

技术标签:

【中文标题】舍入 NSDecimalNumber HalfDown【英文标题】:Rounding NSDecimalNumber HalfDown 【发布时间】:2018-03-21 09:19:10 【问题描述】:

众所周知,在使用精确数字(例如金钱)进行计算时,最好使用NSDecimalNumber 而不是基本的floatdouble。然后,我们有舍入选项:

public enum RoundingMode : UInt 

    case plain // Round up on a tie
    case down // Always down == truncate
    case up // Always up
    case bankers // on a tie round so last digit is even

问题:

如何为此NSDecimalNumber 执行舍入 halfDown(.plain 的倒数,在平局上舍入)?

我原来的问题(你可以跳过这部分)

设置舍入选项(可以更改):Up、Down、HalfUp

我通过这个公式从var p = price exclude tax 计算var pT = price include tax

pT = roundingWithOption(p * tax)

pT 是显示的unitPrice(必须包含税否则企业会坐牢),人们可以自定义它=>我必须计算价格不包括税,通过还原过程

p = roundingWithReverseOption(pT2 / tax)

然后我可以将这个price exclude tax 保存到服务器并取回它以备后用。我们只保存p,因为税收可能会改变,我们可以从其他地方获得税收=> 如果税收改变,pT 可能会有所不同。

save and continue 进程之前和之后的数字如果没有任何变化(四舍五入、税收......)应该是相同的。

选项为.up.down 时很简单,但我不知道如何处理.plain

或者您能否提供一个更好的解决方案来根据自定义价格计算priceExcludeTax?加上折扣和数量会比较复杂,所以我先问一个简单的问题。

【问题讨论】:

对我来说不够清楚。您要税计算器吗?如果是,那么它很广泛。你能谈谈你面临的具体问题吗? 问题只是如何执行舍入halfDown。另一个问题就像一个扩展。 你能举个halfDown的例子吗? 2.5 = 2.0 ? 是的,您的样本是正确的。 0.5 -> 0 -- 0.4 -> 0 -- 0.6 -> 1 如果只想将值转为字符串,可以直接在NumberFormatter上设置舍入。 【参考方案1】:

您可以尝试使用scale 定义所需精度的函数:

func roundSubPlain(_ num: NSDecimalNumber, scale: Int = 0) -> NSDecimalNumber 
    let precision = (1 / pow(10, scale + 2))
    let adjustedDecimalNumber = NSDecimalNumber(decimal: num.decimalValue - precision)
    let handler = NSDecimalNumberHandler(roundingMode: .plain, scale: Int16(scale), raiseOnExactness: true, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true)
    let roundedNum = adjustedDecimalNumber.rounding(accordingToBehavior: handler)

    return roundedNum

所以如果你尝试:

let num1 = NSDecimalNumber(decimal: 5.5)
let num2 = NSDecimalNumber(decimal: 6.25)

你将拥有:

let subNum1 = roundSubPlain(num1) // 5
let subNum2 = roundSubPlain(num1, scale: 1) // 6.2

如果你想为银行家做同样的工作,你可以使用类似的东西:

func roundOdd(_ num: NSDecimalNumber, scale: Int = 0) -> NSDecimalNumber 
    let handler = NSDecimalNumberHandler(roundingMode: .bankers, scale: Int16(scale), raiseOnExactness: true, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true)
    let roundedNum = num.rounding(accordingToBehavior: handler)

    let diff = num.decimalValue - roundedNum.decimalValue
    let gap = (1 / pow(10, scale))

    if abs(diff / gap) == 0.5 
        let adjust: Decimal = diff > 0 ? 1 : -1
        return NSDecimalNumber(decimal: roundedNum.decimalValue + adjust * gap)
     else 
        return roundedNum
    

这当然可以改进,但它确实有效:)

let num = NSDecimalNumber(decimal: 6.15)
let odd = roundOdd(num, scale: 1) // 6.1

【讨论】:

大声笑最好的解决方法。非常聪明,谢谢。反向bankers可以提供奖金吗?

以上是关于舍入 NSDecimalNumber HalfDown的主要内容,如果未能解决你的问题,请参考以下文章

使用 numberFromString 生成 NSDecimalNumber 时如何在没有分数错误的情况下获得舍入值?

NSDecimalNumber 四舍五入在 iOS 11 中不起作用

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

舍入货币字符串

如果 NSDecimalNumber 大于 Uint64,如何将 NSDecimalNumber 转换为字节数组?

将 NSDecimalNumber 变为负数