为什么 std::uniform_real_distribution 不能生成适当的浮动值?
Why won't std::uniform_real_distribution generate proper floating values?
我正在尝试打印随机浮点(32 位)值。为此,我尝试使用 uniform_real_distribution
。我写了下面的代码,
int main()
{
std::random_device rd{};
std::mt19937 gen{rd()};
std::uniform_real_distribution<float> dist(-1e18,1e18);
float random_val = dist(gen);
printf("%.20f\n", random_val);
return 0;
}
现在,输出很奇怪。我得到的只是非常大的数字(总是接近上限或下限),没有分数。以下是我看到的一些输出,
-149399166081040384.00000000000000000000
128349565723082752.00000000000000000000
-323890424458510336.00000000000000000000
802221481969844224.00000000000000000000
817395979383734272.00000000000000000000
他们一直都是这样,我换个界限也无所谓。这里有什么问题?
float
通常是IEEE Single-precision floating-point format,它的工作方式类似于科学记数法,有1个符号位,8个指数位,23+1个小数位。
所以 817395979383734272.0 在内存中存储为 1.41795599 * 2^59。该小数部分只有 ~8 位十进制数字,因为这是它可以压缩到 ~24 位的所有精度。 Mark Ransom 提醒我,因此,所有大于 ~100,000,000 的 float
都将是整数,只是因为它们没有足够的位来存储任何小数部分。
由于小数部分有 ~24 位,这意味着它可以容纳 7.2 位小数的精度。所以前 7 个十进制数字是准确的,第 8 个十进制数字是半准确的,随后的十进制数字在将 float
呈现为文本时实际上是随机的。
817395979383734272.0
^ ^^
| |basically random
| semi-accurate
accurate
对于double
(使用IEEE Double-precision floating-point format),它使用1个符号位、11个指数位和52+1个小数位。这准确地存储了 15.9 位十进制数字,因此仍然可以保留小数部分,直到值大于 ~1,000,000,000,000,000。
David Shwartz 还指出,通常假设随机浮点数会有一些小数和一些大数,但从数学上讲,几乎所有均匀随机生成的浮点数都在最大值的两个数量级内。在您的例子中,是 >1e16 和 <-1e16。这在数学上是正确的,但也会让您感到困惑。
数字没有小数位的原因是32位浮点数不能存储这么大的带小数位的数字。对于 32 位浮点数,您只能获得大约 7 位十进制数字的精度。因此,任何大于 1e7 的数字都会有超过第 7 位的数字,这些数字充其量是不可靠的,最终毫无意义。
在你的例子中,这恰好是一堆零。
您的数字偏大的原因是 [-1e18, 1e18) 范围内的大多数数字都很大。 [-1, 1) 范围内的数字集仅为 [-10, 10) 范围内数字的 10%(大约)。它本身只是 [-100, 100) 范围内数字的 10%(大约)。以此类推。因此,在 [-1e18, 1e18) 范围内获得偶数 5 位数字的机会就是中奖几率。
记住:分布试图在范围内随机地select,而不是生成对应于有效浮点数的随机 32 位。
我正在尝试打印随机浮点(32 位)值。为此,我尝试使用 uniform_real_distribution
。我写了下面的代码,
int main()
{
std::random_device rd{};
std::mt19937 gen{rd()};
std::uniform_real_distribution<float> dist(-1e18,1e18);
float random_val = dist(gen);
printf("%.20f\n", random_val);
return 0;
}
现在,输出很奇怪。我得到的只是非常大的数字(总是接近上限或下限),没有分数。以下是我看到的一些输出,
-149399166081040384.00000000000000000000
128349565723082752.00000000000000000000
-323890424458510336.00000000000000000000
802221481969844224.00000000000000000000
817395979383734272.00000000000000000000
他们一直都是这样,我换个界限也无所谓。这里有什么问题?
float
通常是IEEE Single-precision floating-point format,它的工作方式类似于科学记数法,有1个符号位,8个指数位,23+1个小数位。
所以 817395979383734272.0 在内存中存储为 1.41795599 * 2^59。该小数部分只有 ~8 位十进制数字,因为这是它可以压缩到 ~24 位的所有精度。 Mark Ransom 提醒我,因此,所有大于 ~100,000,000 的 float
都将是整数,只是因为它们没有足够的位来存储任何小数部分。
由于小数部分有 ~24 位,这意味着它可以容纳 7.2 位小数的精度。所以前 7 个十进制数字是准确的,第 8 个十进制数字是半准确的,随后的十进制数字在将 float
呈现为文本时实际上是随机的。
817395979383734272.0
^ ^^
| |basically random
| semi-accurate
accurate
对于double
(使用IEEE Double-precision floating-point format),它使用1个符号位、11个指数位和52+1个小数位。这准确地存储了 15.9 位十进制数字,因此仍然可以保留小数部分,直到值大于 ~1,000,000,000,000,000。
David Shwartz 还指出,通常假设随机浮点数会有一些小数和一些大数,但从数学上讲,几乎所有均匀随机生成的浮点数都在最大值的两个数量级内。在您的例子中,是 >1e16 和 <-1e16。这在数学上是正确的,但也会让您感到困惑。
数字没有小数位的原因是32位浮点数不能存储这么大的带小数位的数字。对于 32 位浮点数,您只能获得大约 7 位十进制数字的精度。因此,任何大于 1e7 的数字都会有超过第 7 位的数字,这些数字充其量是不可靠的,最终毫无意义。
在你的例子中,这恰好是一堆零。
您的数字偏大的原因是 [-1e18, 1e18) 范围内的大多数数字都很大。 [-1, 1) 范围内的数字集仅为 [-10, 10) 范围内数字的 10%(大约)。它本身只是 [-100, 100) 范围内数字的 10%(大约)。以此类推。因此,在 [-1e18, 1e18) 范围内获得偶数 5 位数字的机会就是中奖几率。
记住:分布试图在范围内随机地select,而不是生成对应于有效浮点数的随机 32 位。