简体   繁体   English

我使用哪种 Swift 数据类型作为货币

[英]Which Swift datatype do I use for currency

I have an iOS application that will be performing a lot of basic arithmetic on numbers representing USD currency (eg 25.00 representing $25.00).我有一个 iOS 应用程序,它将对表示美元货币的数字(例如 25.00 表示 25.00 美元)执行大量基本算术运算。

I have gotten into a lot of trouble using the datatype Double in other languages like Java and Javascript so I would like to know the best datatype to use for currency in Swift.我在 Java 和 Javascript 等其他语言中使用数据类型 Double 遇到了很多麻烦,所以我想知道 Swift 中用于货币的最佳数据类型。

Use Decimal , and make sure you initialize it properly !使用Decimal ,并确保正确初始化它

CORRECT正确的


// Initialising a Decimal from a Double:
let monetaryAmountAsDouble = 32.111
let decimal: Decimal = NSNumber(floatLiteral: 32.111).decimalValue
print(decimal) // 32.111  😀
let result = decimal / 2
print(result) // 16.0555 😀


// Initialising a Decimal from a String:
let monetaryAmountAsString = "32,111.01"

let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_US")
formatter.numberStyle = .decimal

if let number = formatter.number(from: monetaryAmountAsString) {
    let decimal = number.decimalValue
    print(decimal) // 32111.01 😀
    let result = decimal / 2.1
    print(result) // 15290.9571428571428571428571428571428571 😀
}

INCORRECT不正确

let monetaryAmountAsDouble = 32.111
let decimal = Decimal(monetaryAmountAsDouble) 
print(decimal) // 32.11099999999999488  😟

let monetaryAmountAsString = "32,111.01"
if let decimal = Decimal(string: monetaryAmountAsString, locale: Locale(identifier: "en_US")) {
    print(decimal) // 32  😟
}

Performing arithmetic operations on Double s or Float s representing currency amounts will produce inaccurate results.对表示货币金额的Double s 或Float s 执行算术运算将产生不准确的结果。 This is because the Double and Float types cannot accurately represent most decimal numbers.这是因为DoubleFloat类型不能准确表示大多数十进制数。 More information here .更多信息在这里

Bottom line: Perform arithmetic operations on currency amounts using Decimal s or Int底线:使用Decimal s 或Int对货币金额执行算术运算

I suggest you start with a typealias for Decimal .我建议您从Decimal的类型typealias开始。 Example:例子:

typealias Dollars = Decimal
let a = Dollars(123456)
let b = Dollars(1000)
let c = a / b
print(c)

Output:输出:

123.456

There's a really nice lib called Money :有一个非常好的库,名为Money

let money: Money = 100
let moreMoney = money + 50 //150

There a lot of nice features besides that, such as type-safe currencies:除此之外还有很多不错的功能,例如类型安全的货币:

let euros: EUR = 100
let dollars: USD = 1500
euros + dollars //Error

Binary operator '+' cannot be applied to operands of type 'EUR' (aka '_Money') and 'USD' (aka '_Money')二元运算符“+”不能应用于“EUR”(又名“_Money”)和“USD”(又名“_Money”)类型的操作数

The Flight-School/Money is probably the best choice for a project full of monies. Flight-School/Money可能是资金充裕的项目的最佳选择。

In theending of README @mattt provides a nice solution for a simple Money type:README结尾@mattt 为简单的 Money 类型提供了一个很好的解决方案:

struct Money {
    enum Currency: String {
      case USD, EUR, GBP, CNY // supported currencies here
    }

    var amount: Decimal
    var currency: Currency
}

Chapter 3 in 'Flight School Guide to Swift Numbers' is provides excellent intro into the topic. 'Flight School Guide to Swift Numbers' 中的第 3 章提供了对该主题的出色介绍。

We use this approach with Currency being a huge enum that has been generated in advance (don't forget a monetary value is just a number without its currency - they both belong together):我们使用这种方法将Currency用作一个预先生成的巨大枚举(不要忘记货币价值只是一个没有货币的数字 - 它们都属于一起):

struct Money: Hashable, Equatable {

    let value: Decimal
    let currency: Currency
}

extension Money {

    var string: String {
        CurrencyFormatter.shared.string(from: self)
    }
}

You need the Decimal and its initializer:您需要Decimal及其初始值设定项:

Decimal(string: "12345.67890", locale: Locale(identifier: "en-US"))

When considering the other answers, please note that Float and Double literals affect the accuracy of the Decimal result.在考虑其他答案时,请注意FloatDouble文字会影响 Decimal 结果的准确性。 The same applies to the NumberFormatter .这同样适用于NumberFormatter

Decimal(5.01) // 5.009999999999998976 ❌

Decimal(floatLiteral: 5.01) // 5.009999999999998976 ❌

NSNumber(floatLiteral: 0.9999999).decimalValue // 0.9999999000000001 ❌

let decimalFormatter = NumberFormatter()
decimalFormatter.numberStyle = .decimal
decimalFormatter.number(from: "0.9999999")?.decimalValue // 0.9999999000000001 ❌

Decimal(string: "5.01", locale: Locale(identifier: "en-US")) // 5.01 ✅
Decimal(string: "0.9999999", locale: Locale(identifier: "en-US")) // 0.9999999 ✅

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM