diff --git a/Sources/SmithyJSON/Reader/Reader.swift b/Sources/SmithyJSON/Reader/Reader.swift index f70752696..9836bd14b 100644 --- a/Sources/SmithyJSON/Reader/Reader.swift +++ b/Sources/SmithyJSON/Reader/Reader.swift @@ -16,6 +16,7 @@ import struct Foundation.Data import struct Foundation.Date import class Foundation.NSNull import class Foundation.NSNumber +import class Foundation.NSDecimalNumber import func CoreFoundation.CFGetTypeID import func CoreFoundation.CFBooleanGetTypeID @@ -126,7 +127,12 @@ public extension Reader { func readIfPresent() throws -> Double? { switch jsonNode { - case .number(let number): return number.doubleValue + case .number(let number): + if let decimalNumber = number as? NSDecimalNumber { + return Double("\(decimalNumber.decimalValue)") + } else { + return number.doubleValue + } case .string(let string): switch string { case "NaN": return .nan diff --git a/Tests/SmithyJSONTests/FloatingPointTests.swift b/Tests/SmithyJSONTests/FloatingPointTests.swift new file mode 100644 index 000000000..693cd3830 --- /dev/null +++ b/Tests/SmithyJSONTests/FloatingPointTests.swift @@ -0,0 +1,46 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@_spi(SmithyReadWrite) import SmithyReadWrite +@_spi(SmithyReadWrite) import SmithyJSON + +// It appears that the doubleValue property on NSNumber & its associated types can sometimes lose +// precision from the original number. +// +// This set of tests is intended to verify that Doubles and Floats can be read accurately from JSON. + +final class FloatingPointTests: XCTestCase { + + static var originalFloats = [Float]() + static var floatData = Data() + static var originalDoubles = [Double]() + static var doubleData = Data() + + // Renders 250 floats & doubles to JSON arrays. Numbers are random so they will + // likely use all available decimal places. + static override func setUp() { + originalFloats = (1..<250).map { _ in Float.random(in: 0.0...1.0) } + originalDoubles = (1..<250).map { _ in Double.random(in: 0.0...1.0) } + floatData = try! JSONEncoder().encode(originalFloats) + doubleData = try! JSONEncoder().encode(originalDoubles) + } + + // Read the floats from JSON using JSONReader, and compare the values to the original. + func test_floatRead() throws { + let reader = try Reader.from(data: Self.floatData) + let floats = try reader.readList(memberReadingClosure: ReadingClosures.readFloat(from:), memberNodeInfo: "", isFlattened: false) + XCTAssert(floats == Self.originalFloats) + } + + // Read the doubles from JSON using JSONReader, and compare the values to the original. + func test_doubleRead() throws { + let reader = try Reader.from(data: Self.doubleData) + let doubles = try reader.readList(memberReadingClosure: ReadingClosures.readDouble(from:), memberNodeInfo: "", isFlattened: false) + XCTAssert(doubles == Self.originalDoubles) + } +}