while 循环中余数运算符 % 的意外结果 - Swift
Unexpected result of remainder operator % in a while loop - Swift
我试图找到一组双打的因子,所以我首先将它们转换为整数,然后使用 %.
找到除数
选项 1
我遇到的问题是,当转换在我的循环中发生时,我丢失了一些信息,特别是如果调用以下函数:
func factors(number: Double) -> [Double] {
var result = [Double]()
var divisor = 0.1
let temporaryIncreasedNumber = Int(number) * 10
while number > divisor {
let theNewDivisor = divisor * 10
print("\(divisor) * \(10) = \(theNewDivisor) for \(divisor), \(temporaryIncreasedNumber) % \(Int(theNewDivisor)) is \(temporaryIncreasedNumber % Int(theNewDivisor))")
if temporaryIncreasedNumber % Int(theNewDivisor) == 0 {
//print ("dividing \(temporaryIncreasedNumber) by \(theNewDivisor) for \(divisor)")
result.append(divisor);
}
divisor += 0.1
}
return result
}
20,所以:
factors(number: 20.0))
如果我查看输出的示例:
1.1 * 10 = 11.0 for 1.1, 200 % 10 is 0
不知何故,当我执行 200 % Int(1.1 * 10)
操作时,Int(1.1 * 10)
给了我 10,而不是 11,我不知道为什么?这当然通过了 A % B == 0
的验证,但它不应该因为 200 % 11 = 2
我在 Playground 中手动执行了以下操作,但效果很好,结果为 200 % 11 = 2
let myX = 20.0
let myY = 1.1
let myCalc = Int(myX * 10.0) % Int(myY * 10.0)
let myCalc2 = 200 % 11
选项 2
或者,如果我只使用双精度值来简单操作,我仍然会得到不一致的值。如果我将代码简化为:
func factors(number: Double) -> [Double] {
var result = [Double]()
var divisor = 0.1
while number > divisor {
let whole = (number/divisor).rounded(.towardZero)
print ("reminder of \(number) by \(divisor) = \((number/divisor) - whole)")
if (number/divisor) - whole == 0.0 {
result.append(divisor);
}
divisor = divisor + 0.1
}
return result
}
factors(number: 20.0)
请注意,我没有明确使用截断提醒,以便我可以检查哪里出了问题。
看下面的操作,结果出乎意料:
reminder of 20.0 by 0.8 = 3.5527136788005e-15
但是如果我在操场上手动执行以下操作:
let test = (20.0/0.8) - (20.0/0.8).rounded(.towardZero)
结果为0!
为什么 while 循环中的行为不同?我的代码有问题吗?
直接联系 Apple 后,他们告知问题是因为强制转换 Int -> Double,反之亦然。在他们的回复中...
Floating-point is binary-based, so 1.1 is not exactly the ideal value
1 1/10, it’s 1.09999….. , and 1.1 *10.0 is really 10.999…...
Double-to-Int conversion truncates
如果您需要更高的精度,他们建议改用 Decimal,我就是这种情况。我通过结合使用 Decimal
和 NSDecimalNumber
操作找到了解决方案(这样我就可以从小数中提取双精度数以对其进行舍入)。
func factors2(number: Double) -> [Decimal] {
var result = [Decimal]()
var divisor = Decimal(0.1)
let decimalNumber = Decimal(number)
while decimalNumber >= divisor {
let operation = (decimalNumber/divisor)
let wholeDecimal = NSDecimalNumber(decimal: operation)
let whole = wholeDecimal.doubleValue.rounded(.towardZero)
print ("reminder of \(decimalNumber) by \(divisor) = \((decimalNumber/divisor) - Decimal(whole))")
if (decimalNumber/divisor) - Decimal(whole) == 0.0 {
result.append(divisor);
}
divisor += 0.1
}
return result
}
print(factors2(number: 20.0))
这个解决方案给出了正确的输出。例子中,20.0的小数因数(从0.1到20.0的计数形式)为:
[0.1, 0.2, 0.4, 0.5, 0.8, 1, 2, 2.5, 4, 5, 10, 20]
我试图找到一组双打的因子,所以我首先将它们转换为整数,然后使用 %.
找到除数选项 1
我遇到的问题是,当转换在我的循环中发生时,我丢失了一些信息,特别是如果调用以下函数:
func factors(number: Double) -> [Double] {
var result = [Double]()
var divisor = 0.1
let temporaryIncreasedNumber = Int(number) * 10
while number > divisor {
let theNewDivisor = divisor * 10
print("\(divisor) * \(10) = \(theNewDivisor) for \(divisor), \(temporaryIncreasedNumber) % \(Int(theNewDivisor)) is \(temporaryIncreasedNumber % Int(theNewDivisor))")
if temporaryIncreasedNumber % Int(theNewDivisor) == 0 {
//print ("dividing \(temporaryIncreasedNumber) by \(theNewDivisor) for \(divisor)")
result.append(divisor);
}
divisor += 0.1
}
return result
}
20,所以:
factors(number: 20.0))
如果我查看输出的示例:
1.1 * 10 = 11.0 for 1.1, 200 % 10 is 0
不知何故,当我执行 200 % Int(1.1 * 10)
操作时,Int(1.1 * 10)
给了我 10,而不是 11,我不知道为什么?这当然通过了 A % B == 0
的验证,但它不应该因为 200 % 11 = 2
我在 Playground 中手动执行了以下操作,但效果很好,结果为 200 % 11 = 2
let myX = 20.0
let myY = 1.1
let myCalc = Int(myX * 10.0) % Int(myY * 10.0)
let myCalc2 = 200 % 11
选项 2
或者,如果我只使用双精度值来简单操作,我仍然会得到不一致的值。如果我将代码简化为:
func factors(number: Double) -> [Double] {
var result = [Double]()
var divisor = 0.1
while number > divisor {
let whole = (number/divisor).rounded(.towardZero)
print ("reminder of \(number) by \(divisor) = \((number/divisor) - whole)")
if (number/divisor) - whole == 0.0 {
result.append(divisor);
}
divisor = divisor + 0.1
}
return result
}
factors(number: 20.0)
请注意,我没有明确使用截断提醒,以便我可以检查哪里出了问题。
看下面的操作,结果出乎意料:
reminder of 20.0 by 0.8 = 3.5527136788005e-15
但是如果我在操场上手动执行以下操作:
let test = (20.0/0.8) - (20.0/0.8).rounded(.towardZero)
结果为0!
为什么 while 循环中的行为不同?我的代码有问题吗?
直接联系 Apple 后,他们告知问题是因为强制转换 Int -> Double,反之亦然。在他们的回复中...
Floating-point is binary-based, so 1.1 is not exactly the ideal value 1 1/10, it’s 1.09999….. , and 1.1 *10.0 is really 10.999…... Double-to-Int conversion truncates
如果您需要更高的精度,他们建议改用 Decimal,我就是这种情况。我通过结合使用 Decimal
和 NSDecimalNumber
操作找到了解决方案(这样我就可以从小数中提取双精度数以对其进行舍入)。
func factors2(number: Double) -> [Decimal] {
var result = [Decimal]()
var divisor = Decimal(0.1)
let decimalNumber = Decimal(number)
while decimalNumber >= divisor {
let operation = (decimalNumber/divisor)
let wholeDecimal = NSDecimalNumber(decimal: operation)
let whole = wholeDecimal.doubleValue.rounded(.towardZero)
print ("reminder of \(decimalNumber) by \(divisor) = \((decimalNumber/divisor) - Decimal(whole))")
if (decimalNumber/divisor) - Decimal(whole) == 0.0 {
result.append(divisor);
}
divisor += 0.1
}
return result
}
print(factors2(number: 20.0))
这个解决方案给出了正确的输出。例子中,20.0的小数因数(从0.1到20.0的计数形式)为:
[0.1, 0.2, 0.4, 0.5, 0.8, 1, 2, 2.5, 4, 5, 10, 20]