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

[SR-10637] Double to String conversion often fails in last digit (e.g. 2/3 != 0.666...7) #53037

Closed
swift-ci opened this issue May 8, 2019 · 2 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella

Comments

@swift-ci
Copy link
Collaborator

swift-ci commented May 8, 2019

Previous ID SR-10637
Radar None
Original Reporter ajo (JIRA User)
Type Bug
Status Resolved
Resolution Invalid
Additional Detail from JIRA
Votes 0
Component/s Standard Library
Labels Bug
Assignee None
Priority Medium

md5: e0426be603bb5b7bf1fd4b94b68afef8

Issue Description:

There seems to be an bug with last digit rounding of Double-to-String conversion.

#!/usr/bin/swift

import Foundation

typealias FloatType = Double

let twoThirds: FloatType = FloatType(2) / FloatType(3)      // 2/3
let twoThirdsString: String = String(twoThirds)             // 0.66...6 (should be ...7)
let formatString: String = String(format: "%lf", twoThirds) // = 0.666667

print("Type: \(FloatType.self)")
print("init(_: \(FloatType.self): \(twoThirdsString)")  // Double: 0.6666666666666666,  Float: 0.6666667
print("init(format: ...): \(formatString)")             // Double: 0.666667             Float: 0.666667


// Should hold if Double -> String conversion holds
assert(twoThirdsString.last == "7")

The Swift script above works when FloatType = Float, but not when FloatType = Double.

@belkadan
Copy link
Contributor

belkadan commented May 8, 2019

This is just how floating-point works: 2/3 cannot be represented exactly, and the closest value to 2/3 in Double is 0.66666666666666662965923251249478198587894439697265625, with the next value up being 0.66666666666666674068153497501043602824211120605468750.

import Foundation

typealias FloatType = Double

let twoThirds: FloatType = FloatType(2) / FloatType(3)
print(String(format: "%.60lf", twoThirds))
print(String(format: "%.60lf", twoThirds + twoThirds.ulp))

@swift-ci
Copy link
Collaborator Author

Comment by Andreas Jönsson (JIRA)

Yes, that's correct but I don't quite fully agree with the conclusion.

The resulting string (in this case, "0.6666666666666666" with 16 significant decimal digits) is chosen so that it's invertible - i.e. converting a Double back and forth to a String can be done deterministically (without introducing rounding errors).

The part I missed was that this is actually specified in the String API init being used - the init with LosslessStringConvertible that's being called in the sample: https://developer.apple.com/documentation/swift/string/2431675-init

So everything is working as specified.

Thanks.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. standard library Area: Standard library umbrella
Projects
None yet
Development

No branches or pull requests

2 participants