Swift:正确解码不精确的十进制

Posted

技术标签:

【中文标题】Swift:正确解码不精确的十进制【英文标题】:Swift: Decode imprecise decimal correctly 【发布时间】:2019-03-12 22:10:25 【问题描述】:

我需要从this question 正确解码(Decodable 协议)一个不精确的十进制值我了解如何正确处理 Decimal 实例化,但是在解码时我该如何做到这一点?

如果尝试将任何数字作为字符串初始化

if let value = try! container.decode(String.self, forKey: .d) 
    self.taxAmount = Decimal(string: value)

我收到Fatal Error: "Expected to decode String but found a number instead."

如果尝试将 130.43 初始化为十进制

if let value = try! container.decode(Decimal.self, forKey: .d) 
    //value.description is 130.43000000000002048
    self.d = Decimal(string: value.description)
    //making subtotal to be also 130.43000000000002048 and not 130.43

有没有办法在解码时使用这两个构造函数?

NSDecimalNumber(string: "1.66") NSDecimalNumber(value: 166).dividing(by: 100) Decimal(166)/Decimal(100) Decimal(sign: .plus, exponent: -2, significand: 166)

这是我从外部服务收到的 JSON 的简化版本:


   "priceAfterTax": 150.00,
   "priceBeforeTax": 130.43,
   "tax": 15.00,
   "taxAmount": 19.57

注意:我无法更改接收到的要解码的内容,我一直在使用十进制数字。

【问题讨论】:

这实际上是一个大问题,在大多数语言中都没有正确处理。通常的解决方案是解析Double,将其存储到Decimal,然后将round 存储到给定数量的Decimal 数字中。 很遗憾你不能。 JSONDecoder 在内部使用NSJSONSerialization,解码为Double。因此,即使您解码 Decimal,它也会首先在内部解码为 Double,因此会丢失精度。正如 Sulthan 指出的那样,有一种解决方法,但由于此实施问题,没有真正的解决方案。 您可以简单地将其编码和解码为 String 并创建一个返回 Decimal 的计算属性 嗨@LeoDabus 我不能,我总是收到一个号码,如果我可以编辑收到的数据相信我,那将是我的第一次尝试。 您可以使用数字格式化程序将接收到的数据转换为字符串 【参考方案1】:

您可以实现自己的解码方法,将您的双精度转换为字符串并使用它来初始化您的十进制属性:


extension LosslessStringConvertible 
    var string: String  .init(self) 


extension FloatingPoint where Self: LosslessStringConvertible 
    var decimal: Decimal?  Decimal(string: string) 


struct Root: Codable 
    let priceAfterTax, priceBeforeTax, tax, taxAmount: Decimal


extension Root 
    public init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.priceAfterTax = try container.decode(Double.self, forKey: .priceAfterTax).decimal ?? .zero
        self.priceBeforeTax = try container.decode(Double.self, forKey: .priceBeforeTax).decimal ?? .zero
        self.tax = try container.decode(Double.self, forKey: .tax).decimal ?? .zero
        self.taxAmount = try container.decode(Double.self, forKey: .taxAmount).decimal ?? .zero
    


let data = Data("""

"priceAfterTax": 150.00,
"priceBeforeTax": 130.43,
"tax": 15.00,
"taxAmount": 19.57

""".utf8)

let decodedObj = try! JSONDecoder().decode(Root.self, from: data)
decodedObj.priceAfterTax   // 150.00
decodedObj.priceBeforeTax  // 130.43
decodedObj.tax             // 15.00
decodedObj.taxAmount       // 19.57

【讨论】:

以上是关于Swift:正确解码不精确的十进制的主要内容,如果未能解决你的问题,请参考以下文章

IEEE 754 二进制浮点数不精确

在 Swift 中使用 PropertyListDecoder 时从 plist 解析浮点值时出现解码错误

IEEE754——4字节实数与十六进制的转换

如何在 C/C++ 中获得大数的精确二进制表示?

js精确计算

Swift 中的 JSON 解码。无法读取数据,因为它的格式不正确