web-dev-qa-db-ja.com

SwiftでDoubleをNSNumberに変換すると精度が失われる

何らかの理由で、my Swiftアプリ内の特定のDoubleがNSNumberに変換するときに問題を引き起こしていますが、そうでないものもあります。私のアプリは小数点以下2桁(価格)のdoubleをNSNumbersに変換する必要があります。 Core Dataを使用して格納および取得できます。たとえば、NSNumberのdoubleValueメソッドを使用して具体的にフォーマットしない限り、79.99などの特定の価格は99.98999999999999と評価されます。

ここでは、デバッガーに表示されるselectedWarranty.price = 79.99

// item.price: NSNumber?       
// selectedWarranty.price: Double?  

item.price = NSNumber(double: selectedWarranty.price!)

変換がどのように行われるかを示すために、いくつかの印刷ステートメントをプログラムしました

Original double: 79.99
Converted to NSNumber: 79.98999999999999
.doubleValue Representation: 79.99

イニシャライザがすべての数値に対して小数点以下2桁を確実に保持できない理由があるかどうか誰かが説明できますか?私は本当にあるべきようにコアデータに価格を保存したいと思います。表示されるたびにフォーマットするのは、あまり便利ではありません。

[〜#〜] update [〜#〜]:データモデルを通じてコアデータオブジェクトをタイプNSDecimalNumberに変換しました。79.99および99.99は問題ではなくなりましたが、異なる数値でより扱いやすい問題が発生します...

Original double: 39.99
Converted to NSDecimalNumber: 39.99000000000001024
10
joelrorseth

まず、いくつかの用語を混乱させています。 79.9899999999999979.99より精度が高く(10進展開が長い)、精度は低くなります(真の値から外れます)。

次に、NSNumberは79.9979.98999999999999も保存しません。 IEEE 754規格に従って値の大きさを格納します。あなたが見ているのは、おそらくその大きさを人間が読める数に変換するために適用される印刷ロジックの結果です。いずれの場合も、固定精度で値を格納するためにFloatまたはDoubleに依存するべきではありません。その性質上、表現可能な値の範囲を広げるために精度を犠牲にします。

価格をセントのIntまたはNSDecimalNumberで表す方がはるかに良いでしょう。

参照してください ダブルまたはフロートを使用して通貨を表現しないのはなぜですか?

これがdoubleがどこでも機能する方法です。小数点以下2桁のみが必要な場合は、値を表示する必要があるときに、2番目の桁の後にポイントを追加するのではなく、整数/長整数を使用することを検討してください。

1
Anton Malyshev