Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected JSON decoding behavior of Int64 #613

Open
kcatigbe-cbs opened this issue May 16, 2024 · 0 comments
Open

Unexpected JSON decoding behavior of Int64 #613

kcatigbe-cbs opened this issue May 16, 2024 · 0 comments

Comments

@kcatigbe-cbs
Copy link

Description

(Forum thread discussing this issue: https://forums.swift.org/t/unexpected-decoding-behavior-of-int64/71762)

When decoding an explicit signed 64 bit value (Int64), if that value is smaller than Int64.min, I am seeing the value being clamped to Int64.min.

I am running this in Xcode 15.3 / M1Pro / Sonoma, but the same behavior happens in 15.2. Not sure when this was introduced, but we have some unit tests now failing due to this.

For example, if my JSON contains "value" = -9223372036854775809, the decoder will assign my struct property the value -9223372036854775808

I believe this might be unexpected because decoding an Int32 with a value less than Int32.min throws a JSON Decoding error:
"Number -2147483649 is not representable in Swift."

Performing a similar decode with JSONSerialization doesn't suffer from this, although it exhibits the classic integer overflow (circling back to the positive value)

This behavior isn't present when testing against Int64.max, as that will throw an error as expected.

Reproduction

do {
    print("-----  MIN 64-Bit Int: \(Int64.min)")
    let json = "{ \"value\": -9223372036854775809 }"
    struct Tester: Codable {
        let value: Int64
    }

    // JSONSerialize
    let serial = try JSONSerialization.jsonObject(with: Data(json.utf8)) as! [String: Any]
    
    let value: NSDecimalNumber = serial["value"] as! NSDecimalNumber
    let v: Int64 = value.int64Value
    print("Type: \(type(of: value)) - value: \(value) - Int64 rep: \(v)")
    
    // Codable
    let tester = try JSONDecoder().decode(Tester.self, from: Data(json.utf8))
    print("Clamped Value: \(tester.value) - Int64.min: \(Int64.min)")
    
} catch {
    print("Error: \(error)")
}

Expected behavior

Expected to throw a JSON Decoding error: "Number -9223372036854775809 is not representable in Swift."

Environment

Swift version 5.10 (swift-5.10-RELEASE)

Additional information

Based on the discussion in the forums and with my own local experimentation, it seems to be related to
func _slowpath_unwrapFixedWidthInteger<T: FixedWidthInteger>(...) where the value is instantiated with:
T(exactly: double), where using Int64(exactly:) has a range of values where it's returning Int64.min.
The boundary seems to be between -9223372036854776832 and -9223372036854776833, as noted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant