四舍五入 NSDecimalNumber HalfDown
Rounding NSDecimalNumber HalfDown
众所周知,在计算精确数字(如金钱)时,最好使用NSDecimalNumber
而不是基本的float
或double
。然后,我们有舍入选项:
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([=16= 的反转],在平局上向下舍入)?
我原来的问题(你可以跳过这部分)
设置舍入选项(可以更改):向上、向下、半向上
我正在通过以下公式从 var p = price exclude tax
计算 var pT = price include tax
:
pT = roundingWithOption(p * tax)
pT 是显示单价(必须含税 否则企业会入狱 lol),人们可以自定义它 => 我必须计算不含税的价格,通过还原过程
p = roundingWithReverseOption(pT2 / tax)
然后我可以将这个 price exclude tax
保存到服务器,稍后取回。我们只保存 p
因为税收可能会改变,我们可以从其他地方征税 => 如果税收改变,pT 可能会有所不同。
save and continue
过程前后的数字应该相同,如果没有任何变化(四舍五入、税收...)。
It's simple when options are .up
and .down
, but I don't know what to do with .plain
.
或者您能否提供更好的解决方案来根据自定义价格计算 priceExcludeTax
?加上折扣和数量会比较复杂,所以我先问一个简单的问题。
您可以试试这个函数,其中 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
众所周知,在计算精确数字(如金钱)时,最好使用NSDecimalNumber
而不是基本的float
或double
。然后,我们有舍入选项:
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([=16= 的反转],在平局上向下舍入)?
我原来的问题(你可以跳过这部分)
设置舍入选项(可以更改):向上、向下、半向上
我正在通过以下公式从 var p = price exclude tax
计算 var pT = price include tax
:
pT = roundingWithOption(p * tax)
pT 是显示单价(必须含税 否则企业会入狱 lol),人们可以自定义它 => 我必须计算不含税的价格,通过还原过程
p = roundingWithReverseOption(pT2 / tax)
然后我可以将这个 price exclude tax
保存到服务器,稍后取回。我们只保存 p
因为税收可能会改变,我们可以从其他地方征税 => 如果税收改变,pT 可能会有所不同。
save and continue
过程前后的数字应该相同,如果没有任何变化(四舍五入、税收...)。
It's simple when options are .up
and .down
, but I don't know what to do with .plain
.
或者您能否提供更好的解决方案来根据自定义价格计算 priceExcludeTax
?加上折扣和数量会比较复杂,所以我先问一个简单的问题。
您可以试试这个函数,其中 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