快速轮次发行

问题描述 投票:0回答:3

我有这个信息。

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

ios swift double rounding
3个回答
5
投票

问题在于数字精度,简单来说,有些数字无法在不损失精度的情况下定义为

Double

您应该使用 NSDecimalNumber:

let num = NSDecimalNumber(string: "9.87")

5
投票

不要使用浮点数或双精度数来表示货币,正是出于这个原因(双关语)。使用 NSDecimalNumber 代替(请参阅我对货币使用哪种 Swift 数据类型)。

NSDecimalNumber 使用起来需要一些努力,但它确实执行以 10 为基数的算术。要正确使用它,您必须阅读一些有关 API 的内容。特别是,要进行舍入,您需要提供一个 NSDecimalNumberHandler (您可以设置一个默认的来使用)。

值得一读的好东西:

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

在你的 Playground 中尝试一下:

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 枚举消失,并被作为 NSDecimalNumber 成员的枚举 RoundingMode 取代。


0
投票

尝试使用此功能:

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

我认为这里最好的做法是显式使用浮点数而不是双精度数,因为如果您只想要两位小数,则不需要双精度数的精度。这个函数只是帮助更准确地舍入它。

© www.soinside.com 2019 - 2024. All rights reserved.