Ios 使用Swift进行浮点减法时的精度损失

Ios 使用Swift进行浮点减法时的精度损失,ios,swift,floating-point-precision,Ios,Swift,Floating Point Precision,我正在尝试使用Swift在我的应用程序中创建浮点数的分数版本。它现在工作得很好,除非它必须构建一个混合数(整数和分数部分) 作为下面的一个例子,当我用0.4调用函数时,它可以正常工作,但不能用1.4,因为它有一个完整的部分(1)。当我将整个部分(整数部分)减去原始数量时,它似乎失去了精度。你可以直接在操场上检查 示例: 0.4 -> 2/5 1.4 -> 1 2/5 2.4 -> 2 2/5 0.5 -> 1/2 0.7 -> 7/10 etc.

我正在尝试使用Swift在我的应用程序中创建浮点数的分数版本。它现在工作得很好,除非它必须构建一个混合数(整数和分数部分)

作为下面的一个例子,当我用0.4调用函数时,它可以正常工作,但不能用1.4,因为它有一个完整的部分(1)。当我将整个部分(整数部分)减去原始数量时,它似乎失去了精度。你可以直接在操场上检查

示例:

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);

在操场上查看,您将看到当您尝试细分1.3或1.4(例如)时会发生什么。

如果您需要依赖精确的数字表示,您可能需要研究“普通”浮点数无法精确表示某些十进制数字。另见

您应该按整数计算,以避免浮点精度问题。因此,首先将浮点值转换为整数

您想要的是以下代码吗

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
func gcd(变量m:Int,变量n:Int)->Int{
如果m字符串{
变量i=0
而数量%1!=0{
数量=数量*10
i+=1
}
变量分子=Int(数量)
var分母=Int(功率(双(10),双(i)))
设除数=gcd(分子、分母)
分子/=除数
分母/=除数
var wholeNumber=0
如果分子>分母{
整数=分子/分母
分子-=分母*整数
}
如果整数>0{
返回“\(整数)\(分子)/\(分母)”
}否则{
返回“\(分子)/\(分母)”
}
}
println(细分(0.4))//2/5
println(细分(1.4))//12/5
println(细分(2.4))//2 2/5
println(细分(0.5))//1/2
println(细分(0.7))//7/10

很可能是的副本。请注意,1.5可以精确地表示为二进制浮点数,但1.4、2.4、3.4。。。不能。@MartinR你说的“二进制浮点”是什么意思?我如何解决我的问题使函数正常工作?我以前也尝试过使用double,但没有成功。
Float
double
使用二进制表示,无法准确存储值1.4。在引用的线程中详细解释了这一切。我不想听起来粗鲁,但在提出解决方案之前,你必须阅读并理解这一点。另一个标准参考是。这正是我所需要的。对我来说非常有效。非常感谢@kishikawa Katsumieen即使这存在精度问题,它们也只是更加隐蔽。例如,
fractionize(0.123458)
给出了
12345801/100000000
。我认为这是不可能的,因为它使用浮点数作为输入。我只是需要它进行成分数量转换,而且从来没有成分数量具有这样的小数数量。所以,它非常适合我。
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