以下python代码根据某些变量计算执行内容的迭代次数。
# a - b - c is always a multiple of d.
i = (a - b - c) / d
while i:
# do stuff
i -= 1
变量都属于同一类型,只有ints
或floats
或其他类型。我担心的是,如果值为floats
,它是否会正常工作。我知道足以始终考虑依赖于精确浮点值的陷阱。但我不知道上述情况是否危险。我可以使用i = int(round((a - b - c) / d))
,但我很好奇能更好地理解浮点数。
这一切都归结为以下几点:a - b - c
是d
的精确倍数。所以我依靠(a-b-c)/d
成为一个值i
,我可以从1
中减去并获得while循环中预期的迭代次数,隐含的假设{{1}变得真实。也就是说,这样的计算倍数可以递减1来恰好达到0?
我不仅想知道它是否不安全,更重要的是,我需要了解浮点数来解决这样的问题?如果有人果断地知道这是否安全,是否有可能解释如何?
答案 0 :(得分:4)
您可以使用decimal module来了解浮点数之间的“隐藏”,例如0.3
:
>>> from decimal import Decimal
>>> Decimal(0.3)
Decimal('0.299999999999999988897769753748434595763683319091796875')
请注意,Python 2.7更改了浮点数的编写方式(repr(f)
如何工作),以便它现在显示最短的字符串,如果执行float(s)
,它将提供相同的浮点数。这意味着Python 2.7中的repr(0.3) == '0.3'
,但早期版本中的repr(0.3) == '0.29999999999999999'
。我之所以提到这一点,是因为当你真的想看看这些数字背后的内容时,它会让事情进一步混淆。
使用十进制模块,我们可以看到浮动计算中的错误:
>>> (Decimal(2.0) - Decimal(1.1)) / Decimal(0.3) - Decimal(3)
Decimal('-1.85037170771E-16')
这里我们可能会期待(2.0 - 1.1) / 0.3 == 3.0
,但是存在一个小的非零差异。但是,如果使用正常浮点数进行计算,则得到零:
>>> (2 - 1.1) / 0.3 - 3
0.0
>>> bool((2 - 1.1) / 0.3 - 3)
False
由于1.85e-16非零,因此结果在某处被舍入:
>>> bool(-1.85037170771E-16)
True
我不确定这种四舍五入的确切位置。
至于循环终止,我可以提供一条线索:for floats less than 253, IEEE 754 can represent all integers:
>>> 2.0**53
9007199254740992.0
>>> 2.0**53 + 1
9007199254740992.0
>>> 2.0**53 + 2
9007199254740994.0
如上所示,可表示数字之间的空间从2 53 到2 54 为2。但是如果你的i
是一个小于2 53 的整数,那么i - 1
也将是一个可表示的整数,你最终会命中0.0
,这被认为是假的在Python中。
答案 1 :(得分:3)
我会给你一个与语言无关的答案(我真的不懂Python)。
您的代码中存在多个潜在问题。首先,这个:
(a - b - c)
如果a
是(例如)10 9 ,b
和c
都是1,那么答案将是10 9 ,而不是10 9 -2(我假设这里有单精度浮点数)。
然后是这个:
i = (a - b - c) / d
如果分子和分母是无法用浮点精确表示的数字(例如0.3和0.1),则结果可能不是精确整数(可能是3.0000001而不是3)。因此,您的循环永远不会终止。
然后是这个:
i -= 1
与上面类似,如果i
当前是10 9 ,那么此操作的结果仍将是10 9 ,因此您的循环将永远不会终止
因此,您应该强烈考虑以整数运算执行所有计算。
答案 2 :(得分:1)
你是对的,零上可能存在非收敛性(至少比你想要的迭代次数多)。为什么不进行测试:while i >= 1
。在这种情况下,与整数一样,如果你的i值低于1,那么循环将结束。