澄清 Python 中的 Decimal 类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20354423/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:16:04  来源:igfitidea点击:

Clarification on the Decimal type in Python

pythonpython-3.xfloating-pointdecimal

提问by Maxime Chéramy

Everybody know, or at least, every programmers should know, that using the floattype could lead to precision errors. However, in some cases, an exact solution would be great and there are cases where comparing using an epsilon value is not enough. Anyway, that's not really the point.

每个人都知道,或者至少每个程序员都应该知道,使用该float类型可能会导致精度错误。但是,在某些情况下,精确的解决方案会很好,并且在某些情况下,使用 epsilon 值进行比较是不够的。无论如何,这不是重点。

I knew about the Decimaltype in Python but never tried to use it. It states that "Decimal numbers can be represented exactly"and I thought that it meant a clever implementation that allows to represent any real number. My first try was:

我知道DecimalPython 中的类型,但从未尝试使用它。它指出“可以精确表示十进制数”,我认为这意味着一个允许表示任何实数的巧妙实现。我的第一次尝试是:

>>> from decimal import Decimal
>>> d = Decimal(1) / Decimal(3)
>>> d3 = d * Decimal(3)
>>> d3 < Decimal(1)
True

Quite disappointed, I went back to the documentation and kept reading:

很失望,我回到文档并继续阅读:

The context for arithmetic is an environment specifying precision [...]

算术的上下文是指定精度的环境 [...]

Ok, so there is actually a precision. And the classic issues can be reproduced:

好的,所以实际上有一个精度。并且可以重现经典问题:

>>> dd = d * 10**20
>>> dd
Decimal('33333333333333333333.33333333')
>>> for i in range(10000):
...    dd += 1 / Decimal(10**10)
>>> dd
Decimal('33333333333333333333.33333333')

So, my question is:is there a way to have a Decimal type with an infinite precision? If not, what's the more elegant way of comparing 2 decimal numbers (e.g. d3 < 1 should return False if the delta is less than the precision).

所以,我的问题是:有没有办法让 Decimal 类型具有无限精度?如果不是,那么比较 2 个十进制数的更优雅的方法是什么(例如,如果 delta 小于精度,则 d3 < 1 应该返回 False)。

Currently, when I only do divisions and multiplications, I use the Fractiontype:

目前,当我只做除法和乘法时,我使用以下Fraction类型:

>>> from fractions import Fraction
>>> f = Fraction(1) / Fraction(3)
>>> f
Fraction(1, 3)
>>> f * 3 < 1
False
>>> f * 3 == 1
True

Is it the best approach? What could be the other options?

这是最好的方法吗?其他选择是什么?

采纳答案by dawg

The Decimal class is best for financial type addition, subtraction multiplication, division type problems:

Decimal 类最适合金融类型的加法、减法、乘法、除法类型的问题:

>>> (1.1+2.2-3.3)*10000000000000000000
4440.892098500626                            # relevant for government invoices...
>>> import decimal
>>> D=decimal.Decimal
>>> (D('1.1')+D('2.2')-D('3.3'))*10000000000000000000
Decimal('0.0')

The Fraction module works well with the rational number problem domain you describe:

Fraction 模块适用于您描述的有理数问题域:

>>> from fractions import Fraction
>>> f = Fraction(1) / Fraction(3)
>>> f
Fraction(1, 3)
>>> f * 3 < 1
False
>>> f * 3 == 1
True

For pure multi precision floating point for scientific work, consider mpmath.

对于用于科学工作的纯多精度浮点数,请考虑mpmath

If your problem can be held to the symbolic realm, consider sympy. Here is how you would handle the 1/3 issue:

如果您的问题可以归为象征领域,请考虑sympy。以下是您将如何处理 1/3 问题:

>>> sympy.sympify('1/3')*3
1
>>> (sympy.sympify('1/3')*3) == 1
True

Sympy uses mpmath for arbitrary precision floating point, includes the ability to handle rational numbers and irrational numbers symbolically.

Sympy 使用 mpmath 进行任意精度浮点运算,包括以符号方式处理有理数和无理数的能力。

Consider the pure floating point representation of the irrational value of √2:

考虑 √2 的无理值的纯浮点表示:

>>> math.sqrt(2)
1.4142135623730951
>>> math.sqrt(2)*math.sqrt(2)
2.0000000000000004
>>> math.sqrt(2)*math.sqrt(2)==2
False

Compare to sympy:

与 sympy 相比:

>>> sympy.sqrt(2)
sqrt(2)                              # treated symbolically
>>> sympy.sqrt(2)*sympy.sqrt(2)==2
True

You can also reduce values:

您还可以减少值:

>>> import sympy
>>> sympy.sqrt(8)
2*sqrt(2)                            # √8 == √(4 x 2) == 2*√2...

However, you can see issues with Sympy similar to straight floating point if not careful:

但是,如果不小心,您会发现 Sympy 的问题类似于直接浮点数:

>>> 1.1+2.2-3.3
4.440892098500626e-16
>>> sympy.sympify('1.1+2.2-3.3')
4.44089209850063e-16                   # :-(

This is better done with Decimal:

使用 Decimal 可以更好地完成此操作:

>>> D('1.1')+D('2.2')-D('3.3')
Decimal('0.0')

Or using Fractions or Sympy and keeping values such as 1.1as ratios:

或者使用 Fractions 或 Sympy 并保留诸如1.1比率之类的值:

>>> sympy.sympify('11/10+22/10-33/10')==0
True
>>> Fraction('1.1')+Fraction('2.2')-Fraction('3.3')==0
True

Or use Rational in sympy:

或者在 sympy 中使用 Rational:

>>> frac=sympy.Rational
>>> frac('1.1')+frac('2.2')-frac('3.3')==0
True
>>> frac('1/3')*3
1

You can play with sympy live.

你可以玩sympy live

回答by NPE

So, my question is: is there a way to have a Decimal type with an infinite precision?

所以,我的问题是:有没有办法让 Decimal 类型具有无限精度?

No, since storing an irrational number would require infinite memory.

不,因为存储一个无理数需要无限内存。

Where Decimalis useful is representing things like monetary amounts, where the values need to be exact and the precision is known a priori.

哪里Decimal是有用的代表之类的货币金额,其中的值必须准确,精度是先验已知的。

From the question, it is not entirely clear that Decimalis more appropriate for your use case than float.

从这个问题来看,并不完全清楚哪个Decimal更适合您的用例而不是float.

回答by jonrsharpe

is there a way to have a Decimal type with an infinite precision?

有没有办法让 Decimal 类型具有无限精度?

No; for any non-empty interval on the real line, you cannot represent all the numbers in the set with infinite precision using a finite number of bits. This is why Fractionis useful, as it stores the numerator and denominator as integers, which canbe represented precisely:

不; 对于实线上的任何非空区间,您不能使用有限位数以无限精度表示集合中的所有数字。这就是Fraction有用的原因,因为它将分子和分母存储为整数,可以精确表示:

>>> Fraction("1.25")
Fraction(5, 4)

回答by William Denman

If you are new to Decimal, this post is relevant: Python floating point arbitrary precision available?

如果您是 Decimal 的新手,这篇文章是相关的:Python 浮点任意精度可用?

The essential idea from the answers and comments is that for computationally tough problems where precision is needed, you should use the mpmathmodule https://code.google.com/p/mpmath/. An important observation is that,

答案和评论的基本思想是,对于需要精度的计算难题,您应该使用mpmath模块https://code.google.com/p/mpmath/。一个重要的观察是,

The problem with using Decimal numbers is that you can't do much in the way of math functions on Decimal objects

使用 Decimal 数的问题是你不能在 Decimal 对象上做很多数学函数