TensorFlow 添加大数

TensorFlow add large numbers

在我的模型中,我可以将小数字与一些大数字相加。但这会产生不正确的值。这是一个简化的例子:

import tensorflow as tf
a = tf.Variable(-1.55786165e+14 ,dtype=np.float32)
b = tf.Variable(-112522840.,dtype=np.float32)
c = tf.Variable(-34. ,dtype=np.float32)

a+b+c

这给出 <tf.Tensor: shape=(), dtype=float32, numpy=-155786280000000.0> 其中正确答案应该是 -155786277522874

我该如何纠正?

可以直接使用 numpy 观察到相同的行为。但是,这仅在使用 dtype=np.float32 而不是 dtype=np.float64 时出现。将您的 dtype 更改为 np.float64 以更正问题。

为了理解其中的原因,您必须了解浮点数是如何存储在内存中的。让我们考虑用单精度和双精度表示时的 a

import numpy as np

a = -1.55786165e+14
a_single = np.array([a], dtype=np.float32)
a_double = np.array([a], dtype=np.float64)
a_single[0], a_double[0], a
# The line above prints:
# (-155786160000000.0, -155786165000000.0, -155786165000000.0)

如您所见,a 在使用单精度时被截断了。但这是为什么呢?

abs(a)的以2为底的对数介于47和48之间。因此,a可以写成-1 * 2^47 * 1.x。在表示浮点数时,必须对指数(48)和分数(x)进行编码。

在我们的例子中,.x 大约等于:

-a / pow(2, 47) - 1

等于0.1069272787267437。现在,我们想要的是将这个数字写成 2 的负幂之和,从 -1 开始。也就是说如果我们用N位来表示的话,我们会把0.1069272787267437 * pow(2, N).

的整数部分存入内存

在单精度中,我们使用N = 23位来表示这个数字。由于0.1069272787267437 * pow(2, 23)的整数部分为896971,其二进制展开为11011010111111001011,长度为20位,因此内存中存储的数为00011011010111111001011.

然而,当使用双精度时,存储在内存中的数字是 0001101101011111100101100000110100101110100000000000。请注意,大量尾随零可能表示存储了 a 的确切值(因为我们不需要更高的精度来表示它),这里就是这种情况。

也就是说,这解释了为什么 a 在表示为单精度浮点数时被截断。同样的推理适用于将 b 添加到 a。由于生成的浮点数的指数将为 47,这意味着您可以在单精度中声明的最小可能精度为 2^47 * 2^-23=2^24,而在双精度中您可以声明的最小可能精度为 [=37] =].由于您使用的是整数,这就解释了为什么您会得到双精度的准确结果和单精度的错误结果。