JSON 解析为 Double 数据类型产生不正确的结果

JSON parsing produce incorrect result for Double datatype

我正在尝试将 class 对象转换为 JSON 字符串以显示在屏幕上,但是 Double 数据类型得到了错误的值。 这是我的代码

struct Wallet: Codable {
  var balance: Double
  var limit: Double
  var currency: String
}

func showJSON() {
  let data = Wallet(balance: Double(200.24), limit: Double(5000), currency: "INR")
  let result = convertToJSONString(data)
  print(result!)
}

func convertToJSONString<T: Codable>(_ response: T) -> String? {
  var jsonString: String?
  let jsonEncoder = JSONEncoder()
  jsonEncoder.outputFormatting = .prettyPrinted
  if let data = try? jsonEncoder.encode(response) {
    jsonString = String(data: data, encoding: .utf8)
  }
  return jsonString
}

//function call
showJSON()

我的输出是

{
  "balance" : 200.24000000000001,
  "limit" : 5000,
  "currency" : "INR"
}

在这里检查余额值,我们提供了 200.24 但转换后显示为 200.24000000000001。有人可以建议这里需要更改什么以获得准确的输出吗?

Note* - You can copy paste this code to playground directly, it will work without any modifications.

尝试使用 Foundation 中的 Decimal 而不是 Double

import Foundation

struct Wallet: Codable {
    var balance: Decimal
    var limit: Decimal
    var currency: String
}

func showJSON() {
    let data = Wallet(
        balance: Decimal(sign: .plus, exponent: -2, significand: 20024),
        limit: 5000,
        currency: "INR"
    )
    let result = convertToJSONString(data)
    print(result!)
}

func convertToJSONString<T: Codable>(_ response: T) -> String? {
    var jsonString: String?
    let jsonEncoder = JSONEncoder()
    jsonEncoder.outputFormatting = .prettyPrinted
    if let data = try? jsonEncoder.encode(response) {
        jsonString = String(data: data, encoding: .utf8)
    }
    return jsonString
}

//function call
showJSON()

或者,您可能希望使用不同的多精度库。参见 Swift-BigInt, swift-numberkit, or BigInt。但这将需要更多的工作(您可能需要围绕这些制作自己的 Decimal 包装器,或使用 Rational 类型或其他东西)。无论你最终选择什么,如果你真的需要这么高的精度,请确保你有足够的测试来确保某处没有细微的问题。

正如@Devid 上面评论的那样,浮点数并不总是准确的,从 DoubleFloatString 的转换会受到影响。

因此,在转换为 String 的过程中,我们始终可以将 Any 数据类型中的 JSON 保留为 print/show。

基本上,它不会进行转换,因此值不会受到影响。

在这里,我只是替换了上面实现中的 convertToJSONString 函数,如

func convertToJSONString<T: Codable>(_ response: T) -> Any? {
  var jsonString: Any?
  let jsonEncoder = JSONEncoder()
  jsonEncoder.outputFormatting = .prettyPrinted
  if let data = try? jsonEncoder.encode(response) {
    jsonString = try? JSONSerialization.jsonObject(with: data, options: [])
  }
  return jsonString
}

使用 JSONSerialization 并获得输出

{
  balance = "200.24";
  currency = INR;
  limit = 5000;
}

用原始 DoubleFloat 值显示大而复杂的 JSON 很有用,要使用它,您始终可以将它们保存在相关数据类型中,这样值就不会得到受影响。