Swift 的浮点数减法精度损失
Loss of precision in float substraction with Swift
我正在尝试使用 Swift 在我的应用程序中创建一个函数来创建浮点数的小数版本。它现在工作得很好,除非它必须构建一个混合数字(整数和小数部分)。
作为下面的示例,当我使用 0.4 调用函数时它工作正常,但使用 1.4 时却不行,因为它有一个完整的部分 (1)。似乎当我将整个部分(integerPart)减去原始数量时它会失去精度。您可以直接在操场上查看。
示例:
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.5 完美配合,但与 1.4、2.4、3.4 等不兼容...因为完全相同。我不知道如何做一个好的减法以使 isInteger 方法工作正常。这是我的 isInteger 函数。我已经测试过了并且工作正常:
func isInteger(quantity: Float) -> Bool {
return floor(quantity) == quantity;
}
查看 playground,您会看到当您尝试细分 1.3 或 1.4 时会发生什么。
如果您需要依赖 精确的 数字表示,您可能需要查看 NSDecimalNumber as "normal" floating point numbers cannot express some decimal numbers exactly. See also this nice tutorial。
您应该按整数计算以避免浮点精度问题。因此,首先将浮点数转换为整数。
你要的是下面的代码吗?
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
我正在尝试使用 Swift 在我的应用程序中创建一个函数来创建浮点数的小数版本。它现在工作得很好,除非它必须构建一个混合数字(整数和小数部分)。
作为下面的示例,当我使用 0.4 调用函数时它工作正常,但使用 1.4 时却不行,因为它有一个完整的部分 (1)。似乎当我将整个部分(integerPart)减去原始数量时它会失去精度。您可以直接在操场上查看。
示例:
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.5 完美配合,但与 1.4、2.4、3.4 等不兼容...因为完全相同。我不知道如何做一个好的减法以使 isInteger 方法工作正常。这是我的 isInteger 函数。我已经测试过了并且工作正常:
func isInteger(quantity: Float) -> Bool {
return floor(quantity) == quantity;
}
查看 playground,您会看到当您尝试细分 1.3 或 1.4 时会发生什么。
如果您需要依赖 精确的 数字表示,您可能需要查看 NSDecimalNumber as "normal" floating point numbers cannot express some decimal numbers exactly. See also this nice tutorial。
您应该按整数计算以避免浮点精度问题。因此,首先将浮点数转换为整数。
你要的是下面的代码吗?
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