Last active
December 31, 2017 08:59
-
-
Save denkeni/f07e5dcdba40746e3bb017d11c0fd012 to your computer and use it in GitHub Desktop.
Double to String Precision Issue (Swift & Obj-C)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let value : Double = 1234567890.1234567 | |
// 1. String format | |
"\(value)" == "1234567890.1234567" // false | |
"\(value)" // "1234567890.12346" | |
value.description | |
String(format: "%f", value) == "1234567890.1234567" // false | |
String(format: "%f", value) // "1234567890.123457" | |
String(format: "%.7f", value) == "1234567890.1234567" // true! | |
String(format: "%.7f", value) // "1234567890.1234567" | |
String(format: "%.8f", value) == "1234567890.1234567" // false | |
String(format: "%.8f", value) // "1234567890.12345672" wtf | |
String(format: "%.9f", value) == "1234567890.1234567" // false | |
String(format: "%.9f", value) // "1234567890.123456717" wtf | |
// 2. NSNumber & NumberFormatter | |
let number = NSNumber(value: value) | |
number.doubleValue == value // true | |
number.stringValue == "1234567890.1234567" // false | |
number.stringValue // "1234567890.123457" | |
let nf = NumberFormatter() | |
nf.numberStyle = .decimal | |
nf.usesGroupingSeparator = false | |
// 2.1 Significant Digits = 17 | |
nf.usesSignificantDigits = true | |
nf.maximumSignificantDigits = 14 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.1235" | |
nf.maximumSignificantDigits = 15 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.12346" | |
nf.maximumSignificantDigits = 16 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.12346" wtf | |
nf.maximumSignificantDigits = 17 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.12346" wtf | |
// 2.2 minimum and maximum number of (integer, fraction) digits = (10, 7) | |
nf.usesSignificantDigits = false | |
nf.maximumIntegerDigits = 10 | |
nf.maximumFractionDigits = 4 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.1235" | |
nf.maximumFractionDigits = 5 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.12346" | |
nf.maximumFractionDigits = 6 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.12346" wtf | |
nf.maximumFractionDigits = 7 | |
nf.string(from: number) == "1234567890.1234567" // false | |
nf.string(from: number) // "1234567890.12346" wtf | |
// 3. NSDecimalNumber & Decimal | |
let num = NSDecimalNumber(value: value) | |
num.doubleValue == value // false! | |
num.stringValue == "1234567890.1234567" // false | |
num.stringValue // "1234567890.1234569216" wtf | |
num.description == "1234567890.1234567" // false | |
var decimal = Decimal(value) // 1234567890.1234569216 | |
let decimalString = NSDecimalString(&decimal, nil) // "1234567890.1234569216" | |
decimalString == "1234567890.1234567" // false | |
// 4. Break into fractional and integral parts | |
let sep = modf(value) | |
sep.0 // integral:1234567890 | |
sep.1 // fractional:0.1234567165374756 | |
sep.1 == value - sep.0 // true | |
sep.1 == 0.1234567 // false! | |
// 5. JSONEncoder (weird workaround) | |
let array = [1234567890.1234567] // [1234567891.123457] wtf | |
array[0] == 1234567890.1234567 // true | |
let jsonEncoder = JSONEncoder() | |
if let data = try? jsonEncoder.encode(array) { | |
if var str = String(data: data, encoding: .utf8) { | |
// "[1234567891.1234567]" | |
str.removeFirst() | |
str.removeLast() | |
str == "1234567890.1234567" // true! | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment