当我在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”这样的“大”小数,不应该吗?
答案 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...
您的问题有几种解决方法:
nextafter
。我之前的回答HERE nextafter
的更多信息
重要的概念是的值相对于值(如果您正在递增或递减)。
这可以在这里看到:
>>> 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-17
或1-(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
。