2016-06-13 6 views
0

私はこの情報を持っています。印刷params2即決のラウンド

結果が今

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

"payamount_credit": 9.87は、私はすべての方法を試してみました"payamount_credit": 9.869999999999999

です

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

ラウンドは存在しますが、同じように動作します。

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

Double(round(1000*9.87)/1000)

すべての奇妙なことはこれだけ特定の数(9.87)で起こるということで、神秘的な何かがあります。

プレイグラウンドスクリーン。

enter image description here

+1

これは浮動小数点値です。 :) http://programmers.stackexchange.com/questions/101163/what-c​​auses-floating-point-rounding-errors –

+0

新しいトークンをプライベートにする必要があります。あなたはそれらをサンプル出力に含めました。 – Brett

答えて

4

正確に(しゃれ)このような理由のために、通貨を表現するために浮動小数点数または倍精度を使用しないでください。代わりにNSDecimalNumberを使用してください(Which Swift datatype do I use for currencyを参照)。

NSDecimalNumberは使用するのに多少の努力をしますが、ベース10の算術演算を実行します。正しく使用するには、APIで少し読んでください。特に、丸めを行うには、NSDecimalNumberHandlerを指定する必要があります(デフォルトのものを使用するように設定できます)。

読むために良いもの:

私は遊び場が予期しない結果を示しと思いこれは、NSDecimalNumberの内部値をDouble Befに強制しているためですそれを表示している鉱石(私はこの好奇心を見つけるので、私は代替的または権威のある説明を歓迎する)。しかし、内部表現は正しいはずです。

はあなたの遊び場でこれを試してみてください:

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" 

は、そのフロートは、数のより正確な表現であると仮定しないでください、それだけで良く、ここで働くことを起こります。 NSDecimalNumberレルムで計算(税金、割引、出荷など)を維持すると、それらは正確になります。


スウィフト3/Xcodeのベータ1回のボーナス:

遊び場の動作は同じですが、偉大なAPIの名前変更ロデオがに上記のコードを変更する変更:

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

変更はこちらグローバルなNSRoundingMode列挙型がなくなり、NSDecimalNumberのメンバーである列挙型RoundingModeに置き換えられます。

+0

動作しません、私の答えを確認してください – jose920405

+0

あなたは 'Double'で初期化していますので、すでに精度を失ってしまっています。文字列で初期化します。 – Jim

3

問題はnumeric precissionにあります。単純に言えば、精度を損なうことなくDoubleとして定義することはできません。

代わりにあなたがNSDecimalNumberを使用する必要があります。

let num = NSDecimalNumber(string: "9.87") 
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 

私はここで行うための最善のことは、明示的に精度ので、代わりに二重のフロートを使用することだと思います2桁の小数点以下桁数が必要な場合は、2桁の数字は必要ありません。この関数はちょっとだけ正確に丸めます。

+0

私の編集を参照してください。 @ jose920405 – Ike10