“This is the 19th day of my participation in the Gwen Challenge in November. See details: The Last Gwen Challenge in 2021”.
NSDecimalNumber is different from Decimal
NSDecimalNumber is a subclass of NSNumber, which is much more powerful than NSNumber, rounding, rounding, automatically removing useless zeros from numeric values, and so on. Because NSDecimalNumber is more precise, it takes longer than the basic data type, so there are trade-offs. Apple officially recommends using it for currency and high-precision scenarios.
Typically we use the formatter NSDecimalNumberHandler to set the format that it needs to constrain, and then build the desired NSDecimalNumber
let ouncesDecimal: NSDecimalNumber = NSDecimalNumber(value: doubleValue)
let behavior: NSDecimalNumberHandler = NSDecimalNumberHandler(roundingMode: mode,
scale: Int16(decimal),
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false)
let roundedOunces: NSDecimalNumber = ouncesDecimal.rounding(accordingToBehavior: behavior)
Copy the code
NSDecimalNumber and Decimal are essentially seamlessly bridged, Decimal is a value type Struct, NSDecimalNumber is a reference type Class, It looks like NSDecimalNumber is more versatile, but if you just want to set bits and round them in a way that requires Decimal, you can do it with better performance, So I think NSDecimalNumber is only used as a backup when Decimal can’t do something.
In general, the relationship between NSDecimalNumber and Decimal is similar to that between NSString and String.
Decimal
The correct way to use
The correct use ofjson
Deserialization pairDecimal
Perform an assignment — useObjectMapper
When we declare a Decimal property and then assign it to a JSON string, we find that the precision is still lost. Why is this?
struct Money: Codable {
let amount: Decimal
let currency: String
}
let json = "{"amount": 9021.234891,"currency":"CNY"}"
let jsonData = json.data(using: .utf8)!
let decoder = JSONDecoder(a)let money = try! decoder.decode(Money.self, from: jsonData)
print(money.amount)
Copy the code
The answer is simple: Our JSONDecoder() uses JSONSerialization() internally to deserialize. The logic is very simple. When it comes to the number 9021.234891, it will treat it as a Double. Converting a Double to Decimal can then be successful, but it is already a lost Double, and the converted Decimal type is also lost.
For this problem, we must be able to control the deserialization process. My current choice is to use ObjectMapper, which has the flexibility to control serialization and deserialization using custom rules.
ObjectMapper does not support Decimal by default. We can create a TransformType that supports Decimal as follows:
open class DecimalTransform: TransformType {
public typealias Object = Decimal
public typealias JSON = Decimal
public init(a) {}
open func transformFromJSON(_ value: Any?) -> Decimal? {
if let number = value as? NSNumber {
return Decimal(string: number.description)
} else if let string = value as? String {
return Decimal(string: string)
}
return nil
}
open func transformToJSON(_ value: Decimal?). -> Decimal? {
return value
}
}
Copy the code
We then apply this TransformType to the properties we want to transform
struct Money: Mappable {
var amount: Decimal?
var currency: String?
init(a){}init?(map: Map){}mutating func mapping(map: Map) {
amount <- (map["amount"].DecimalTransform())
currency <- map["currency"]}}Copy the code
The correct use ofDecimal
The initialization mode of
There are many ways to initialize Decimal, we can pass in integer values, we can pass in floating-point values, we can pass in strings, and I think the correct way to initialize Decimal is to use strings.
The graph above should be a pretty straightforward illustration of why I think so. The reason for this is similar to the previous antisequence problem, and also because when we pass in a Double, Swift loads it once, which causes it to lose its precision, initializing Decimal from a Double that has lost its precision, It makes sense that this Decimal is missing precision
reference
- Decoding money from JSON in Swift