为什么从内存中插入“随机”数字通常非常大?

问题描述 投票:1回答:1

有时在开发过程中,例如使用C代码,您可能会意外地在数组的最后一个元素之后建立索引,从而导致读取本质上是“随机”的内存块。我经常处理double的数组,并且注意到发生这种情况时,“随机”内存产生的double通常非常大,比1e+300大。我不知道为什么会这样。

如果用于解释double的64位确实是随机的,我希望double的指数从0均匀分布到308(忽略指数的符号),使用科学(指数)表示法将浮点数布置在内存中的方式。当然,存储器中随机选择的位的值本身并不是随机分布的,而是与设置这些值的任何过程对应的有意义的状态。

[为了研究这种效果,我编写了以下Python 3脚本,该脚本绘制了从“随机”但未使用的内存中提取的真正随机生成的doubledouble的分布:

import random, struct
import numpy as np
import matplotlib.pyplot as plt

N = 10000

def random_floats(N=1):
    return np.array(struct.unpack('d'*N, bytes(random.randrange(256) for _ in range(8*N))))

def exp_hist(a, label=None):
    a = a[~np.isnan(a)]
    a = a[~np.isinf(a)]
    a = a[a != 0]
    if len(a) == 0:
        print('Zeros only!')
        return
    a = np.abs(np.log10(np.abs(a)))
    plt.hist(a, range=(0, 350), density=True, alpha=0.8, label=label)

# Floats generated from uniformly random bits
a = random_floats(N)
exp_hist(a, 'random')

# Floats generated from memory content
a = np.empty(N)
exp_hist(a, 'memory')

plt.xlabel('exponent')
plt.legend()
plt.savefig('plot.png')

运行此脚本的典型结果如下:

“

真正随机生成的double的指数实际上是均匀分布的。

根据内存内容解释的double的指数非常小或非常大。实际上,很多未使用的内存被清零,导致很多0值,这是有道理的。但是,正如我经常从弹跳内存访问中体验到的那样,1e+300附近也会出现很多值。

我想解释大量的非常大的double

关于运行脚本的注意事项

如果您想亲自尝试该脚本,请注意,可能需要多次运行它才能显示有趣的内容。可能发生的情况是,从内存内容中读取的每个数字都是0,在这种情况下,它会告诉您。如果这种情况反复发生,请尝试降低N(使用的double的数量)。

memory floating-point double random-access
1个回答
1
投票

您可能会在内存中找到许多不同的东西,但是其中有惊人的数目映射到非常大或非常小的浮点数,无穷大或NaN。在下文中,“ FP”表示IEEE 754 64位二进制浮点。

首先,由于已经在问题注释中进行了讨论,因此请考虑地址。 64位地址通常所有指数位为零(存储器的低端)或所有指数位为高(存储器的高端,通常是堆栈地址)。如果所有指数位都很高,则为无穷大或NaN,程序似乎会忽略它。如果所有指数位均为零,则它是次普通数或零。次普通数均小于2.3E-308,记作指数308。

现在考虑32位整数,这是另一种非常常见的数据形式。映射到有限FP的负32位二进制补码整数为-1048577或更小。像-42或-1这样的数字映射到NaN,程序会忽略它们。类似地,中值正整数的所有指数位都为零,因此映射到次正数,映射到直方图的大指数端。即使是很小的正常数也对应于令人惊讶的大整数。例如,1e-300的前32位具有整数值27,618,847。

对于指针和整数,对于具有相同值的所有指数位(全为零或全为1)存在强烈的偏见。全都是NaN或无穷大,程序不计算在内。全零是次正规的,被视为非常大的指数。

© www.soinside.com 2019 - 2024. All rights reserved.