Uploaded image for project: 'Swift'
  1. Swift
  2. SR-7054

JSONDecoder Decimal precision error

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Open
    • Priority: Medium
    • Resolution: Unresolved
    • Component/s: Foundation
    • Labels:
      None
    • Environment:

      Xcode 9.2 (9C40b)
      Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)

      Same output with 4.1 swift-DEVELOPMENT-SNAPSHOT-2018-02-20-a-ubuntu16.04.

      Description

      Decoding decimals from a JSON structure returns incorrect numbers.

      See the following example:

      #!/usr/bin/env swift
      
      import Foundation
      
      struct DoubleItem : Codable \{
       var name: String
       var price: Double
      }
      
      struct DecimalItem : Codable \{
       var name: String
       var price: Decimal
      }
      
      let jsonString = """
      \{ "name": "Gum ball", "price": 46.984765 }
      """
      let jsonData = jsonString.data(using: .utf8)!
      
      let decoder = JSONDecoder()
      do \{
       let doubleItem = try decoder.decode(DoubleItem.self, from: jsonData)
       print(doubleItem)
      
      let decimalItem = try decoder.decode(DecimalItem.self, from: jsonData)
       print(decimalItem)
      } 
      catch \{
       print(error)
      }
      
      

      Output:

      DoubleItem(name: "Gum ball", price: 46.984765000000003)
      
      DecimalItem(name: "Gum ball", price: 46.98476500000001024)
      
      

      Expected result for the DecimalItem should be: 46.984765 

      I know that floating point values can not be represented precisely, but decimals should keep their original values, am I wrong? Is this a Swift foundation bug or an intended behavior? Note that encoding the same value with the JSONEncoder will provide the correct value in the JSON.  

      My actual problem is that the JSONEncoder and JSONDecoder classes are working inconsistently.

      If the encoder will output the value as a number, I'd expect that the decoder can decode the exact same value from that if I use the decimal type in my model. My other idea is that the encoder should transform and save the value into a string type inside the JSON instead of the current representation, this could be supported with a decimal coding strategy.

       

      let encoder = JSONEncoder()
      encoder.decimalEncodingStrategy = .string //outputs as string: "46.984765"
      encoder.decimalEncodingStrategy = .precise //outputs as number: 46.984765
      encoder.decimalEncodingStrategy = .lossy //current output like: 46.984765
      
      
      let encoder = JSONDecoder()
      encoder.decimalDecodingStrategy = .string //decoded value from json string:  46.984765
      encoder.decimalDecodingStrategy = .precise //decoded value from json number: 46.984765
      encoder.decimalDecodingStrategy = .lossy //current value from number:  46.98476500000001024
      

       

      Please share your thoughts about this idea.

        Attachments

          Activity

            People

            Assignee:
            bendjones Ben D. Jones
            Reporter:
            tiborbodecs Tibor Bödecs
            Votes:
            36 Vote for this issue
            Watchers:
            36 Start watching this issue

              Dates

              Created:
              Updated: