"This is the 12th day of my participation in the November Update Challenge. For details of the event, please refer to: 2021 Last Update Challenge ".
Difference between NSDecimalNumber and Decimal
NSDecimalNumber
NSNumber
Is a subclass of , NSNumber
more powerful than , rounding, rounding, automatically removing the useless 0 in front of the value after input, etc. Due to the high NSDecimalNumber
precision , it will take more time than the basic data type, so it needs to be weighed, Apple official It is recommended to be used in currency and scenarios that require high precision.
Usually we will use NSDecimalNumberHandler
this formatter to set the format that needs to be constrained, and then construct the requiredNSDecimalNumber
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)
复制代码
NSDecimalNumber
It is Decimal
basically a seamless bridge with . It Decimal
is a value type Struct
and NSDecimalNumber
a reference type Class
. It seems NSDecimalNumber
that the setting function is more abundant, but if only the number of digits and the rounding method are required, it Decimal
can be fully satisfied, and the performance will be better. , so I think it should NSDecimalNumber
only Decimal
be considered as a backup if a feature cannot be implemented.
In general, theNSDecimalNumber
relationship with is similar to the relationship with .Decimal
NSString
String
Decimal
the correct way to use
Correct use of json
deserialization Decimal
to assign values -- useObjectMapper
When we declare a Decimal
property , and then use a json
string to assign it, we will find that the precision is still lost, why is there such a result?
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()
let money = try! decoder.decode(Money.self, from: jsonData)
print(money.amount)
复制代码
The answer is simple: what we use is used JSONDecoder()
internally JSONSerialization()
for deserialization, and its logic is very simple. When it encounters 9021.234891
this number, it will not hesitate to regard it as a Double
type, and then Double
convert it to Decimal
It can be successful, But at this time, the precision has been Double
lost and the converted Decimal
type is naturally lost.
For this problem, we must be able to control its deserialization process. My current option is to use ObjectMapper
it, which can flexibly control the serialization and deserialization process using custom rules.
ObjectMapper
It is not supported Decimal
by , we can customize a supported Decimal
type TransformType
, as follows:
open class DecimalTransform: TransformType {
public typealias Object = Decimal
public typealias JSON = Decimal
public init() {}
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
}
}
复制代码
Then apply this TransformType
to the properties we need to transform
struct Money: Mappable {
var amount: Decimal?
var currency: String?
init() { }
init?(map: Map) { }
mutating func mapping(map: Map) {
amount <- (map["amount"], DecimalTransform())
currency <- map["currency"]
}
}
复制代码
Correct use Decimal
of initialization
Decimal
There are multiple initialization methods. We can pass in integer values, floating point values, and strings for initialization. I think the correct initialization method should be to use strings.
The above picture should be very simple and clear why I think so. The reason is similar to the previous deserialization problem, but also because when we passed Double
in , Swift carried it once, and this time the carry caused it Precision is lost, according to the Double
initialization Decimal
, it is not difficult to understand that this Decimal
is the loss of precision