Swift:正确解码不精确的小数

Swift:正确解码不精确的小数,swift,decodable,nsdecimalnumber,Swift,Decodable,Nsdecimalnumber,我需要正确地解码(Decodableprotocol)一个不精确的十进制值,因为我知道如何正确地处理十进制实例化,但是在解码时我该怎么做呢 如果尝试将任何数字初始化为字符串 if let value = try! container.decode(String.self, forKey: .d) { self.taxAmount = Decimal(string: value) } 我发现致命错误:“本应解码字符串,但却找到了一个数字。” 如果尝试将130.43初始化为十进制 if l

我需要正确地解码(
Decodable
protocol)一个不精确的十进制值,因为我知道如何正确地处理十进制实例化,但是在解码时我该怎么做呢

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

if let value = try! container.decode(String.self, forKey: .d) {
    self.taxAmount = Decimal(string: value)
}
我发现
致命错误:“本应解码字符串,但却找到了一个数字。”

如果尝试将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(字符串:“1.66”)
  • NSDecimalNumber(值:166)。除以(100)
  • Decimal(166)/Decimal(100)
  • Decimal(符号:。加号,指数:-2,有效位:166)
下面是我从外部服务收到的JSON的简化版本:

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

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

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







这实际上是一个在大多数语言中都没有正确处理的大问题。通常的解决方案是解析
Double
,将其存储到
Decimal
中,然后
round
将其舍入到给定的
Decimal
位数。遗憾的是,您不能
JSONDecoder
在内部使用
NSJSONSerialization
,它解码为
Double
。因此,即使您解码一个
十进制
,它首先在内部解码为一个
双精度
,因此精度会丢失。正如Sulthan指出的,有一个解决办法,但由于这个实现问题,没有真正的解决方案。你可以简单地将其编码和解码为字符串,然后创建一个返回小数的计算属性。Hi@LeoDabus我不能,我总是收到一个数字,如果我能编辑收到的数据,相信我,那将是我第一次尝试。你可以使用数字格式化程序将收到的数据转换成字符串
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