Loss of precision on float with Swift
I am trying to create a function to create a floating point version in my application using Swift. It works perfectly right now, except when it has to create a mixed number (whole and fractional part).
As an example below, when I call the function with 0.4 it works fine, but not 1.4 because it has an integer part (1). It seems when I subtract the whole (integerPart) part by the original value, it loses precision. You can check this right on the playground.
Examples:
0.4 -> 2/5
1.4 -> 1 2/5
2.4 -> 2 2/5
0.5 -> 1/2
0.7 -> 7/10
etc...
func fractionize(quantity: Float) -> String {
let integerPart: Float = floor(quantity);
var numerator: Float = quantity - integerPart;
var firstNumerator: Float = numerator;
var denominator: Float = 1;
if (isInteger(quantity)) {
return "\(Int(integerPart))";
} else {
do {
denominator++;
numerator = firstNumerator * denominator;
println(numerator);
} while (!isInteger(numerator) && denominator <= 10);
if (integerPart > 0) {
if (isInteger(numerator)) {
return "\(integerPart) \(Int(numerator))/\(Int(denominator))";
} else {
return "\(quantity) couldn't be fractionized. Result = \(integerPart) \(numerator) / \(denominator)";
}
} else {
if (isInteger(numerator)) {
return "\(Int(numerator))/\(Int(denominator))";
} else {
return "\(quantity) couldn't be fractionized. Result = \(numerator) / \(denominator)";
}
}
}
}
fractionize(1.4);
As an additional example, it works fine with 1.5, but not 1.4, 2.4, 3.4, etc ... thanks to the same. I don't know how to do a good substitution so that the isInteger method works fine. This is my isInteger function. I've already tested it and it works great:
func isInteger(quantity: Float) -> Bool {
return floor(quantity) == quantity;
}
Go to the playground and you will see what happens when you try to split like 1.3 or 1.4.
source to share
You have to compute an integer to avoid the floating point problem. So convert float to integer first.
Is this what you want in the following code?
func gcd(var m: Int, var n: Int) -> Int {
if m < n {
(m, n) = (n, m)
}
if n == 0 {
return m
} else if m % n == 0 {
return n
} else {
return gcd(n, m % n)
}
}
func fractionize(var quantity: Float) -> String {
var i = 0
while quantity % 1 != 0 {
quantity = quantity * 10
i += 1
}
var numerator = Int(quantity)
var denominator = Int(pow(Double(10), Double(i)))
let divisor = gcd(numerator, denominator)
numerator /= divisor
denominator /= divisor
var wholeNumber = 0
if numerator > denominator {
wholeNumber = numerator / denominator
numerator -= denominator * wholeNumber
}
if wholeNumber > 0 {
return "\(wholeNumber) \(numerator)/\(denominator)"
} else {
return "\(numerator)/\(denominator)"
}
}
println(fractionize(0.4)) // 2/5
println(fractionize(1.4)) // 1 2/5
println(fractionize(2.4)) // 2 2/5
println(fractionize(0.5)) // 1/2
println(fractionize(0.7)) // 7/10
source to share
If you need to rely on exact representations of numbers, you can look into NSDecimalNumber , since "normal" floating point numbers cannot express certain decimal numbers exactly. See also this good tutorial .
source to share