任何人都可以解释模运算符在Python中的工作原理吗?
我无法理解为什么3.5 % 0.1 = 0.1
。
答案 0 :(得分:59)
实际上,3.5 % 0.1
0.1
并非如此。您可以非常轻松地测试它:
>>> print(3.5 % 0.1)
0.1
>>> print(3.5 % 0.1 == 0.1)
False
实际上,在大多数系统中,3.5 % 0.1
为0.099999999999999811
。但是,在某些版本的Python上,str(0.099999999999999811)
为0.1
:
>>> 3.5 % 0.1
0.099999999999999811
>>> repr(3.5 % 0.1)
'0.099999999999999811'
>>> str(3.5 % 0.1)
'0.1'
现在,您可能想知道为什么3.5 % 0.1
是0.099999999999999811
而不是0.0
。这是因为通常的浮点舍入问题。如果您还没有阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic,那么您应该 - 或者至少是此特定问题的简要Wikipedia摘要。
另请注意,3.5/0.1
不是34
,而是35
。因此,3.5/0.1 * 0.1 + 3.5%0.1
为3.5999999999999996
,甚至关闭至3.5
。这对于模数的定义来说非常重要,它在Python中是错误的,而且几乎与其他所有编程语言一样。
但Python 3在那里得救。大多数了解//
的人都知道你是如何在整数之间进行“整数除法”的,但是没有意识到它是如何在任何类型之间进行模数兼容的划分。 3.5//0.1
为34.0
,因此3.5//0.1 * 0.1 + 3.5%0.1
(至少在小的舍入误差范围内)3.5
。这已经被移植到2.x,因此(取决于您的确切版本和平台)您可以依赖于此。而且,如果没有,您可以使用divmod(3.5, 0.1)
,它返回(在舍入错误内)(34.0, 0.09999999999999981)
一直回到时间的迷雾中。当然,您仍然期望这是(35.0, 0.0)
,而不是(34.0, almost-0.1)
,但由于舍入错误,您不能拥有它。
如果您正在寻找快速解决方案,请考虑使用Decimal
类型:
>>> from decimal import Decimal
>>> Decimal('3.5') % Decimal('0.1')
Decimal('0.0')
>>> print(Decimal('3.5') % Decimal('0.1'))
0.0
>>> (Decimal(7)/2) % (Decimal(1)/10)
Decimal('0.0')
这不是一个神奇的灵丹妙药 - 例如,只要操作的确切值在基数10中无法有限表示,你仍然必须处理舍入误差 - 但是舍入误差与案例的排列更好人类的直觉预计会有问题。 (Decimal
优于float
的优点还在于您可以指定显式精度,跟踪有效数字等,并且在2.4至3.3的所有Python版本中它实际上相同,而细节约float
同时改变了两次。只是它不完美,因为那是不可能的。)但是当你提前知道你的数字在10号基数中都是完全可以表示的时候,它们不会需要比你配置的精度更多的数字,它会起作用。
答案 1 :(得分:5)
Modulo为您提供分区的rest
。 3.5
除以0.1
后,35
会为0
提供余下的>>> from decimal import Decimal
>>> Decimal('3.5') / Decimal('0.1')
Decimal('35')
>>> Decimal('3.5') % Decimal('0.1')
Decimal('0.0')
。但由于浮点数是基于2的幂,数字不是完全,你会得到舍入错误。
如果您需要精确划分十进制数,请使用十进制模块:
0.1
当我受到抨击时,我的答案误导了整个故事:
0.1
略大于>>> '%.50f' % 0.1
'0.10000000000000000555111512312578270211815834045410'
3.5
如果你将浮动0.1
除以这个数字,你会得到几乎0.11
的其余部分。
让我们从数字1
开始,继续在两个0.1
数字之间添加零,以便在保持大于>>> '%.10f' % (3.5 % 0.101)
'0.0660000000'
>>> '%.10f' % (3.5 % 0.1001)
'0.0966000000'
>>> '%.10f' % (3.5 % 0.10001)
'0.0996600000'
>>> '%.10f' % (3.5 % 0.100001)
'0.0999660000'
>>> '%.10f' % (3.5 % 0.1000001)
'0.0999966000'
>>> '%.10f' % (3.5 % 0.10000001)
'0.0999996600'
>>> '%.10f' % (3.5 % 0.100000001)
'0.0999999660'
>>> '%.10f' % (3.5 % 0.1000000001)
'0.0999999966'
>>> '%.10f' % (3.5 % 0.10000000001)
'0.0999999997'
>>> '%.10f' % (3.5 % 0.100000000001)
'0.1000000000'
的同时使其变小。
0.1
最后一行给人的印象是我们最终达到了>>> '%.20f' % (3.5 % 0.100000000001)
'0.09999999996600009156'
,但更改格式字符串揭示了真实的本质:
3.5 % 0.1 = 0.1
python的默认浮点格式根本没有显示足够的精度,因此3.5 % 0.1 = 35.0
和3.5 % 0.100000... = 0.999999...
。它确实是3.5 / 0.100000... = 34.999999....
和34.9999...
。如果是除法,您最终会得到完全结果,因为35.0
被最终舍入为0.1
。
有趣的事实:如果您使用的数字略小于0
并执行相同的操作,您最终会得到一个略大于>>> 1.0 - 0.9
0.09999999999999998
>>> 35.0 % (1.0 - 0.9)
7.771561172376096e-15
>>> '%.20f' % (35.0 % (1.0 - 0.9))
'0.00000000000000777156'
的数字:
3.5
使用C ++,您甚至可以显示0.1
除以浮点35
不是#include <iostream>
#include <iomanip>
int main(int argc, char *argv[]) {
// double/float, rounding errors do not cancel out
std::cout << "double/float: " << std::setprecision(20) << 3.5 / 0.1f << std::endl;
// double/double, rounding errors cancel out
std::cout << "double/double: " << std::setprecision(20) << 3.5 / 0.1 << std::endl;
return 0;
}
,而是更小一些。
3.5 / 0.1
在Python中35
为您提供3.5 / 0.100000... = 34.9999999...
的确切结果,因为舍入错误相互抵消。它确实是34.9999...
。而且35
已经过了很长时间,以至于你最终得到{{1}}。 C ++程序很好地显示了这一点,因为你可以混合double和float并使用浮点数的精度。
答案 2 :(得分:1)
它与浮点运算的不精确性有关。 3.5 % 0.1
让我0.099999999999999811
,所以Python认为0.1最多分为3.5次,最多34次,剩下0.099999999999999811。我不确定究竟是用什么算法来实现这个结果,但这就是要点。