Python算术用小数字

时间:2012-05-11 17:03:22

标签: python numerical

当我在Python中使用小数字进行算术运算时,我得到以下意外结果:

>>> sys.float_info
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)
>>> (1. - (1.e-17) ) < 1.
False

我知道浮点数不具有无限精度,但它应该能够处理像“1e-17”这样的“大”小数,不应该吗?

7 个答案:

答案 0 :(得分:8)

>>> import numpy as np
>>> np.nextafter(1., 0.)
0.99999999999999989

这是1.之后的0.

方向的下一个浮点数

我认为1. - 1.e-17距离1.更近,而numpy.nextafter(1., 0.)更接近1. - 1.e-17,所以当评估{{1}}时,它会给你1个回来。使用更远的其他漂浮物是没有意义的。

相关问题 - &gt; Increment a python floating point value by the smallest possible amount

答案 1 :(得分:4)

首先,让我们回顾epsilon的返回值中sys.float_info的真实含义。

Epsilon(或)是 0.5 +≠0.5且0.5 - ≠0.5

的最小数字

Python告诉你,导致0.5可重复递增或递减的最小数字是epsilon=2.220446049250313e-16 - ,但这只是值0.5 。您正尝试按1.0增加1.0e-17。这是一个较大的值(1.0对0.5)增加的数字小于0.5(1.0e-17 vs 2.2e-16)。由于1.0e-17的增量值比1.0的相对值小一个数量级,因此大致偏离了一个数量级。

你可以在这里看到:

这些会更改0.5

的值
>>> 0.5+sys.float_info.epsilon
0.5000000000000002
>>> 0.5-sys.float_info.epsilon
0.4999999999999998

这些值不会:

>>> 0.5+sys.float_info.epsilon/10.0
0.5
>>> 0.5-sys.float_info.epsilon/10.0
0.5
>>> 5.0+sys.float_info.epsilon
5.0
>>> 5.0-sys.float_info.epsilon
5.0

<强>解释

IEEE 754定义了目前大多数标准计算机上使用的浮点格式(专业计算机或库可能使用不同的格式。)IEEE 754的64 bit format使用53位精度来计算,52存储到浮点值的尾数。由于您可以使用固定的52/53位,因此尾数的大小和精度会随着更大/更小的值而变化。那么随着浮点数的相对大小的变化,这些变化就会发生变化。 0.5的值与1.0的值和100.0的值不同。

由于各种非常好的和特定于平台的原因(存储和表示,舍入等),即使可能使用较小的数字,epsilon也被定义为使用52位精度64位浮点格式。由于大多数Python实现都使用C double float for float,因此可以证明:

>>> 2**-52==sys.float_info.epsilon
True

查看平台平台将执行多少位:

>>> 0.5 + 2.0**-53
0.5000000000000001
>>> 0.5 - 2.0**-53
0.4999999999999999
>>> 0.5 + 2.0**-54
0.5                           # fail for 0.5 + 54 bits...
>>> 0.5 - 2.0**-54
0.49999999999999994           # OK for minus
>>> 0.5 - 2.0**-55
0.5                           # fail  for 0.5 minus 55 bits...

您的问题有几种解决方法:

  1. 您可以使用nextafter的C99概念来计算适当的epsilon值。对于Python,要么使用numpy,要么使用Decimal类来计算nextafter。我之前的回答HERE
  2. 中有关nextafter的更多信息
  3. 使用整数。 64位整数将清楚地处理17个数量级的epsilon值而不进行舍入。
  4. 使用任意精度数学库。 Decimal属于标准Python发行版。
  5. 重要的概念是的值相对于值(如果您正在递增或递减)。

    这可以在这里看到:

    >>> numpy.nextafter(0.0,1.0)-0.0
    4.9406564584124654e-324                 # a relative epsilon value of 4.94e-324
    >>> numpy.nextafter(0.01,1.0)-0.01
    1.7347234759768071e-18                  # 1e-17 would still work...
    >>> numpy.nextafter(0.1,1.0)-0.1
    1.3877787807814457e-17                  # 1e-17 would >>barely<< work...
    >>> numpy.nextafter(0.5,1.0)-0.5
    1.1102230246251565e-16                  # a relative epsilon value of 1.1e-16
    >>> numpy.nextafter(500.0,501.0)-500.0
    5.6843418860808015e-14                  # relative epsilon of 5.6e-14
    >>> numpy.nextafter(1e17,1e18)-1e17
    16.0                                    # the other end of the spectrum...
    

    所以你可以看到1e-17可以方便地增加0.0到0.1之间的值,但不会有更多的值。如您所见,1e17的相对值为16.

答案 2 :(得分:3)

  它应该能够处理像1e-17这样的“大”小数字,不应该吗?

不一定(取决于数字)。 float无法准确代表1e-171-(1e-17)。对于后者, 所代表的最接近的数字是1

我建议你阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

答案 3 :(得分:3)

如果您需要此级别的精确度,请考虑Decimal module

>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')
Decimal('0.999999999999999990')
>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')<decimal.Decimal(1.0)
True

>>> decimal.Decimal(1.0)-decimal.Decimal('1.0e-17')<1.0
True

小心使用最后一个因为你可以获得转换错误。

其他人建议What Every Computer Scientist Should Know About Floating-Point Arithmetic.,我也建议Don’t Store That in a Float

答案 4 :(得分:0)

你可以处理那些。请注意

>>> 1.e-17 == 0
False

>>> 1.e-17 + 1.e-18
1.1e-17

你根本无法处理1-1e-17,因为尾数不适合有限精度

答案 5 :(得分:0)

>>> from decimal import Decimal
>>> Decimal('1')-Decimal(str(10**-17)) < Decimal('1')
True

使用decimal module,以达到这种准确性!

答案 6 :(得分:0)

您有epsilon=2.220446049250313e-16所以(1. - (1.e-17) ) = 1是正常的,因为1.e-17 < epsilon