浮点数上的 Python 模数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14763722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python modulo on floats
提问by beruic
Can anyone explain how the modulo operator works in Python?
I cannot understand why 3.5 % 0.1 = 0.1.
谁能解释模运算符在 Python 中的工作原理?我不明白为什么3.5 % 0.1 = 0.1。
采纳答案by abarnert
Actually, it's not true that 3.5 % 0.1is 0.1. You can test this very easily:
其实,这是不正确的3.5 % 0.1是0.1。你可以很容易地测试这个:
>>> print(3.5 % 0.1)
0.1
>>> print(3.5 % 0.1 == 0.1)
False
In actuality, on most systems, 3.5 % 0.1is 0.099999999999999811. But, on some versions of Python, str(0.099999999999999811)is 0.1:
实际上,在大多数系统上,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'
Now, you're probably wondering why 3.5 % 0.1is 0.099999999999999811instead of 0.0. That's because of the usual floating point rounding issues. If you haven't read What Every Computer Scientist Should Know About Floating-Point Arithmetic, you should—or at least the brief Wikipediasummary of this particular issue.
现在,您可能想知道为什么3.5 % 0.1是0.099999999999999811而不是0.0. 那是因为通常的浮点舍入问题。如果您还没有阅读每个计算机科学家应该知道的关于浮点运算的知识,您应该——或者至少是这个特定问题的简短维基百科摘要。
Note also that 3.5/0.1is not 34, it's 35. So, 3.5/0.1 * 0.1 + 3.5%0.1is 3.5999999999999996, which isn't even closeto 3.5. This is pretty much fundamental to the definition of modulus, and it's wrong in Python, and just about every other programming language.
另请注意,这3.5/0.1不是34,而是35。那么,3.5/0.1 * 0.1 + 3.5%0.1是3.5999999999999996,这是不甚至接近到3.5。这几乎是模数定义的基础,它在 Python 和几乎所有其他编程语言中都是错误的。
But Python 3 comes to the rescue there. Most people who know about //know that it's how you do "integer division" between integers, but don't realize that it's how you do modulus-compatible division between anytypes. 3.5//0.1is 34.0, so 3.5//0.1 * 0.1 + 3.5%0.1is (at least within a small rounding error of) 3.5. This has been backported to 2.x, so (depending on your exact version and platform) you may be able to rely on this. And, if not, you can use divmod(3.5, 0.1), which returns (within rounding error) (34.0, 0.09999999999999981)all the way back into the mists of time. Of course you still expected this to be (35.0, 0.0), not (34.0, almost-0.1), but you can't have that because of rounding errors.
但是 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),但是由于舍入错误,您不能拥有它。
If you're looking for a quick fix, consider using the Decimaltype:
如果您正在寻找快速修复,请考虑使用以下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')
This isn't a magical panacea — for example, you'll still have to deal with rounding error whenever the exact value of an operation isn't finitely representable in base 10 - but the rounding errors line up better with the cases human intuition expects to be problematic. (There are also advantages to Decimalover floatin that you can specify explicit precisions, track significant digits, etc., and in that it's actually the same in all Python versions from 2.4 to 3.3, while details about floathave changed twice in the same time. It's just that it's not perfect, because that would be impossible.) But when you know in advance that your numbers are all exactly representable in base 10, and they don't need more digits than the precision you've configured, it will work.
这不是灵丹妙药——例如,当一个操作的确切值不能以 10 为基数有限表示时,你仍然需要处理舍入误差——但舍入误差更符合人类直觉所期望的情况有问题。(Decimalover 的另一个优点float是您可以指定显式精度、跟踪有效数字等,并且在从 2.4 到 3.3 的所有 Python 版本中它实际上是相同的,而有关的详细信息同时float更改了两次。它是只是它并不完美,因为那是不可能的。)但是当您事先知道您的数字都可以完全以 10 为基数表示,并且它们不需要比您配置的精度更多的数字时,它就会起作用。
回答by jjlin
It has to do with the inexact nature of floating point arithmetic. 3.5 % 0.1gets me 0.099999999999999811, so Python is thinking that 0.1 divides into 3.5 at most 34 times, with 0.099999999999999811 left over. I'm not sure exactly what algorithm is being used to achieve this result, but that's the gist.
它与浮点运算的不精确性质有关。3.5 % 0.1得到我0.099999999999999811,所以 Python 认为 0.1 最多可分为 3.5 34 次,剩下 0.099999999999999811。我不确定使用什么算法来实现这个结果,但这就是要点。
回答by bikeshedder
Modulo gives you the restof a division. 3.5divided by 0.1should give you 35with a rest of 0. But since floats are based on powers of two the numbers are not exactand you get rounding errors.
Modulo 给你rest一个除法。3.5除以0.1应该给你35剩下的0。但是由于浮点数基于 2 的幂,因此数字不准确,并且会出现四舍五入错误。
If you need your division of decimal numbers to be exact use the decimal module:
如果您需要精确划分十进制数,请使用小数模块:
>>> from decimal import Decimal
>>> Decimal('3.5') / Decimal('0.1')
Decimal('35')
>>> Decimal('3.5') % Decimal('0.1')
Decimal('0.0')
As I am being bashed that my answer is misleading here comes the whole story:
当我被抨击我的回答具有误导性时,整个故事就来了:
The Python float 0.1is slightly larger than one-tenth:
Python 浮点数0.1略大于十分之一:
>>> '%.50f' % 0.1
'0.10000000000000000555111512312578270211815834045410'
If you divide the float 3.5by such number you get a rest of almost 0.1.
如果您将浮点数3.5除以这样的数字,您将得到几乎剩下的0.1.
Let's start with the number 0.11and continue adding zeros in between the two 1digits in order to make it smaller while keeping it larger than 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'
The last line gives the impression that we finally have reached 0.1but changing the format strings reveals the true nature:
最后一行给人的印象是我们终于达到了,0.1但改变格式字符串揭示了真实的本质:
>>> '%.20f' % (3.5 % 0.100000000001)
'0.09999999996600009156'
The default float format of python simply does not show enough precision so that the 3.5 % 0.1 = 0.1and 3.5 % 0.1 = 35.0. It really is 3.5 % 0.100000... = 0.999999...and 3.5 / 0.100000... = 34.999999..... In case of the division you even end up with the exactresult as 34.9999...is ultimatively rounded up to 35.0.
python 的默认浮点格式根本没有显示足够的精度,因此3.5 % 0.1 = 0.1和3.5 % 0.1 = 35.0. 它确实是3.5 % 0.100000... = 0.999999...和3.5 / 0.100000... = 34.999999....。在分工的情况下,你甚至与最终确切的结果34.9999...是ultimatively四舍五入到35.0。
Fun fact: If you use a number that is slightly smaller than 0.1and perform the same operation you end up with a number that is slightly larger than 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'
Using C++ you can even show that 3.5divided by the float 0.1is not 35but something a little smaller.
使用 C++,你甚至可以证明3.5除以浮点数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;
}
In Python 3.5 / 0.1gives you the exact result of 35because the rounding errors cancel out each other. It really is 3.5 / 0.100000... = 34.9999999.... And 34.9999...is ultimatively so long that you end up with exactly 35. The C++ program shows this nicely as you can mix double and float and play with the precisions of the floating point numbers.
在 Python 中3.5 / 0.1,35由于舍入误差相互抵消,因此为您提供了确切的结果。真的是3.5 / 0.100000... = 34.9999999...。并且34.9999...最终如此之长,以至于您最终得到了35. C++ 程序很好地展示了这一点,因为您可以混合使用 double 和 float 并使用浮点数的精度。

