提问人:sten 提问时间:9/16/2021 最后编辑:sten 更新时间:9/16/2021 访问量:1046
numpy 浮点数的精度限制?
Precision limits of numpy floats?
问:
给定序列
1/1, 1/2, 1/3, ... , 1/n
如果我使用 numpy.float16,我如何计算在哪个点上我无法以精度 E 区分两个连续元素 1/i 和 1/i+1?即什么是“我”?
其他 np-floats 呢?
最小的E是多少?以及如何计算它的“i”?
例如,如果 E = 0.01,我可以区分 1/9 和 1/10,但不能区分 1/10 和 1/11,因为:
1/9 = 0.111
1/10 = 0.100
1/11 = 0.091
0.111 - 0.100 = 0.01 >= E
0.100 - 0.091 = 0.009 < E
i = 10
以更抽象的方式,给定 f(i) np.floatXX 中可表示的最大 'i' 是多少?
有趣的是,实际中的精度比计算的要差: /逻辑中断的地方/
for i in range(int(1e3),int(12e6)) :
if not np.floatXX(1/i) > np.floatXX(1/(i+1)) :
print(i); break
float32: 11864338
float16: 1464
答:
不要加 1,而是将分母加倍。您可以放心地假设它是某个二进制数。这里有一个简单的方法:
one = np.float64(1.0)
two = np.float64(2.0)
n = one
bits = 0
while one + n != one:
bits += 1
n /= two
你从开始,否则你会得到带你超过分辨率的位数。bits = 0
最后,你得到 ,这是 IEEE-754 编码的 64 位浮点数中的位数。bits = 53
这意味着,对于任何以有效的二进制科学记数法编码的数字,ULP(最小精度单位)约为 。具体来说,其中数字四舍五入到其最高位。您将无法解析浮点数中较小的相对变化。n * 2**-53
n
奖励:为其他浮点类型运行上述代码可提供:
float16 (half): 11 bits
float32 (single): 24 bits
float64 (double): 53 bits
float96 (sometimes longdouble): 80 bits
float128 (when available): 113 bits
您可以修改上面的代码以适用于任何目标号码:
target = np.float16(0.0004883)
one = np.float16(1.0)
two = np.float16(2.0)
n = two**(np.floor(np.log2(target)) - one)
bits = 0
while target + n != target:
bits += 1
n /= two
结果 (ULP) 由下式给出,因为循环在失去分辨率后停止。这与我们开始的原因相同。在这种情况下:n * 2
bits = 0
>>> n * two
5e-07
如果您预先知道尾数中的位数,则可以完全缩短计算。所以对于 , where , 你可以做float16
bits = 11
>>> two**(np.floor(np.log2(target)) - np.float16(bits))
5e-07
在这里阅读更多内容:
评论
n
n
n * two
我的另一个答案提供了你实际提出的问题背后的理论,但需要一些不平凡的解释。这是缺少的步骤:
给定一些整数,你可以写成i
1 / i - 1 / (i + 1) =
(i + 1 - i) / (i * (i + 1)) =
1 / (i * (i + 1)) =
1 / (i**2 + i)
要找到在某些二进制表示中低于 ULP 的此类方法,您可以直接使用我的另一个答案。i
1 / (i**2 + i)
1 / i
的 ULP 由下式给出1 / i
ulp = 2**(floor(log2(1 / i)) - (bits + 1))
你可以尝试找到一个这样的i
1 / (i**2 + i) < 2**(floor(log2(1 / i)) - (bits + 1))
1 / (i**2 + i) < 2**floor(log2(1 / i)) / 2**(bits + 1)
2**(bits + 1) < (i**2 + i) * 2**floor(log2(1 / i))
It's not trivial as written because of the operation, and Wolfram Alpha runs out of time. Since I am cheap and don't want to buy Mathematica, let's just approximate:floor
2**(bits + 1) < (i**2 + i) * 2**floor(log2(1 / i))
2**(bits + 1) < (i**2 + i) / i
2**(bits + 1) < i + 1
You may be off by one or so, but you should see that around , the difference stops being resolvable. And indeed for the 11 bit mantissa of , we see:i = 2**(bits + 1) - 1
float16
>>> np.float16(1 / (2**12 - 1)) - np.float16(1 / (2**12))
0.0
The actual number is a tiny bit less here (remember that approximation where we took away the ):floor
>>> np.float16(1 / (2**12 - 5)) - np.float16(1 / (2**12 - 4))
0.0
>>> np.float16(1 / (2**12 - 6)) - np.float16(1 / (2**12 - 5))
2.4e-07
As you noted in your comments, isi
>>> 2**12 - 6
4090
You can compute the exact values for all the other floating point types in a similar manner. But that is indeed left as an exercise for the reader.
评论
1 / i
ulp = 2**(floor(log2(1 / i)) - (bits + 1))
float16
bits
float16
1 / (i**2 + i) < 2**(floor(log2(1 / i)) - (bits + 1))
1 / (i**2 + i) < 2**(floor(log2(1 / i)) - (bits - 1))
评论