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.

+3


source to share


2 answers


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

      

+2


source


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 .



+2


source







All Articles