快速回合问题

时间:2016-06-13 16:17:52

标签: ios swift double rounding

我有这个信息。

let params2: [String: AnyObject] = [
    "app_token": myapptoken,
    "member_access_token": accessToken!,
    "pay_process": 0,
    "payamount_credit": 9.87 //hardcode
]

打印params2

  

结果是

["app_token": myapptoken, "member_access_token": accessToken, "payamount_credit": 9.869999999999999, "pay_process": 0]

"payamount_credit": 9.87现在是"payamount_credit": 9.869999999999999

我已经尝试了所有存在的方法但行为相同。

NSString(format: "%.\2f", 9.87)

Double(round(1000*9.87)/1000)

最奇怪的是,只有这个特定的数字(9.87)发生,才是神秘的。

  

游乐场屏幕。

enter image description here

3 个答案:

答案 0 :(得分:4)

不要使用浮点数或双打来代表货币,这是因为(双关语)这个原因。请改用NSDecimalNumber(参见Which Swift datatype do I use for currency)。

NSDecimalNumber需要花费一些精力才能使用,但它确实执行了基数为10的算术运算。要正确使用它,您必须阅读API。特别是,要进行舍入,您需要提供NSDecimalNumberHandler(您可以设置要使用的默认值)。

阅读的好东西:

认为 Playground显示意外的结果,因为它在显示之前将NSDecimalNumber的内部值强制转换为Double(我会欢迎替代或权威的解释,因为我觉得这很好奇)。但是内部表示应该是正确的。

在你的游乐场试试这个:

let myRoundingBehavior = NSDecimalNumberHandler(roundingMode: .RoundBankers,
scale: 2, raiseOnExactness: true, raiseOnOverflow: true, raiseOnUnderflow: true, raiseOnDivideByZero: true)

NSDecimalNumber.setDefaultBehavior(myRoundingBehavior)

let integerPrice = NSDecimalNumber(mantissa: 987, exponent: -2, isNegative: false)

integerPrice             // 9.870000000000001
Float(integerPrice)      // 9.87
Double(integerPrice)     // 9.870000000000001
integerPrice.description // "9.87"

不要认为Float是一个更准确的数字表示,它恰好在这里工作得更好。如果您在NSDecimalNumber领域保留计算(如税,折扣,运费等),它们将是准确的。

Swift 3 / Xcode beta 1奖金:

Playground行为仍然相同,但更改Great API Renaming Rodeo会将上述代码更改为:

let myRoundingBehavior = NSDecimalNumberHandler(roundingMode: .roundBankers,
                                                scale: 2,
                                                raiseOnExactness: true,
                                                raiseOnOverflow: true,
                                                raiseOnUnderflow: true,
                                                raiseOnDivideByZero: true)

这里的变化是全局NSRoundingMode枚举消失了,并被枚举RoundingMode取代,它是NSDecimalNumber的成员。

答案 1 :(得分:3)

问题在于numeric precission,简单地说,有些数字不能定义为Double而不会失去精确度。

相反,您应该使用NSDecimalNumber

let num = NSDecimalNumber(string: "9.87")

答案 2 :(得分:1)

尝试使用此功能:

let price: Double = 9.87

func roundDouble(num: Double) -> Float {

  //Create NSDecimalNumberHandler to specify rounding behavior
  let numHandler = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundDown, scale: 2, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false)

  let roundedNum = NSDecimalNumber(double: num).decimalNumberByRoundingAccordingToBehavior(numHandler)

  //Convert to float so you don't get the endless repeating decimal.
  return Float(roundedNum)

}

roundDouble(price) //9.87

我认为在这里做的最好的事情是显式使用float而不是double,因为如果你只需要两个小数位,则不需要double的精度。这个功能只是为了更准确地帮助它。