Quantos números duplos existem entre 0,0 e 1,0?

93

Isso é algo que está na minha mente há anos, mas nunca parei para perguntar antes.

Muitos geradores de números (pseudo) aleatórios geram um número aleatório entre 0,0 e 1,0. Matematicamente, existem números infinitos neste intervalo, mas doubleé um número de ponto flutuante e, portanto, tem uma precisão finita.

Então, as perguntas são:

  1. Quantos doublenúmeros existem entre 0,0 e 1,0?
  2. Existem tantos números entre 1 e 2? Entre 100 e 101? Entre 10 ^ 100 e 10 ^ 100 + 1?

Nota: se faz diferença, estou interessado na definição de Java doubleem particular.

poligenelubrificantes
fonte

Respostas:

68

Os Java doubles estão no formato IEEE-754 , portanto, eles têm uma fração de 52 bits; entre quaisquer duas potências adjacentes de dois (incluindo uma e exclusiva da próxima), haverá, portanto, 2 elevado à 52ª potência doubles diferentes (ou seja, 4503599627370496 deles). Por exemplo, esse é o número de doubles distintos entre 0,5 incluídos e 1,0 excluídos, e exatamente esse número também está entre 1,0 incluído e 2,0 excluídos e assim por diante.

Contar doublesentre 0,0 e 1,0 é mais difícil do que entre potências de dois, porque há muitas potências de dois incluídas nessa faixa e, também, entra-se nas questões espinhosas dos números desnormalizados. 10 dos 11 bits dos expoentes cobrem o intervalo em questão, então, incluindo números desnormalizados (e eu acho que alguns tipos NaN), você teria 1024 vezes o doubles entre potências de dois - não mais do que 2**62no total de qualquer maneira . Excluindo o desnormalizado etc., acredito que a contagem seria de 1.023 vezes 2**52.

Para um intervalo arbitrário como "100 a 100,1" é ainda mais difícil porque o limite superior não pode ser representado exatamente como a double(não sendo um múltiplo exato de qualquer potência de dois). Como uma aproximação útil, uma vez que a progressão entre as potências de dois é linear, você poderia dizer que o referido intervalo é o 0.1 / 64décimo do intervalo entre as potências circundantes de dois (64 e 128), então você esperaria cerca de

(0.1 / 64) * 2**52

distinto doubles - que vem para 7036874417766.4004... dar ou tirar um ou dois ;-).

Alex Martelli
fonte
@Alex: apenas para observar, quando escrevi 100 a 100,1 eu escrevi errado. Eu quis dizer 100 a 101. Basicamente, entre N e N + 1 para N. arbitrário
poligenelubrificantes
4
@Alex: deixe-me ver se entendi: não pode haver mais do que 2**64valores duplos possíveis (já que é um tipo de 64 bits) e, aparentemente, uma proporção ENORME desses valores está entre os dois 0..1?
poligenelubrificantes
9
@poligene, sim e sim - especificamente, cerca de um quarto dos valores possíveis (para qualquer representação de ponto flutuante "normal" de qualquer base e expoente vs comprimentos de fração) estão entre 0,0 e 1,0 (outro quarto entre 1,0 e infinito, e o metade restante na metade negativa do eixo real). Essencialmente, metade dos valores do expoente (com uma tendência normal, metade dentro de sua faixa) representam potências negativas da base, portanto, números <1,0.
Alex Martelli
8
@polygenelubricants: para muitas aplicações, o intervalo entre 0 e 1 é muito, muito mais importante e interessante do que o intervalo entre 100 e 101, por isso obtém uma parcela maior dos valores. Por exemplo, na física, muitas vezes você tem que lidar com valores ridiculamente pequenos, como a constante gravitacional de Newton em 6,67e-11. Ter uma boa precisão lá é mais útil do que entre 100 e 101. Leia floating-point-gui.de para obter mais informações.
Michael Borgwardt
1
Você também pode dimensionar qualquer número entre 0,0 e 1,0, mantendo o controle da escala separadamente, resultando em menos erros no cálculo. É bom quando toda a linha numérica pode ser mapeada entre dois números!
codekaizen
42

Todo doublevalor cuja representação está entre 0x0000000000000000e 0x3ff0000000000000está no intervalo [0,0, 1,0]. São (2 ^ 62 - 2 ^ 52) valores distintos (mais ou menos um par, dependendo de você contar os pontos de extremidade).

O intervalo [1.0, 2.0] corresponde às representações entre 0x3ff0000000000000e 0x400000000000000; são 2 ^ 52 valores distintos.

O intervalo [100,0, 101,0] corresponde às representações entre 0x4059000000000000e 0x4059400000000000; são 2 ^ 46 valores distintos.

Não há duplos entre 10 ^ 100 e 10 ^ 100 + 1 . Nenhum desses números é representável em precisão dupla, e não há duplos que caiam entre eles. Os dois números de precisão dupla mais próximos são:

99999999999999982163600188718701095...

e

10000000000000000159028911097599180...
Stephen Canon
fonte
+1, para uma resposta exata e bem fundamentada. (Se você for exigente quanto à contagem dos pontos finais, lembre-se de que +0,0 e -0,0 têm representações distintas.)
Jim Lewis
1
1, um final de torção! Parecia que estava lendo um roteiro de M. Night Shyamalan!
poligenelubrificantes de
7

Outros já explicaram que existem cerca de 2 ^ 62 duplos no intervalo [0,0, 1,0].
(Não é realmente surpreendente: há quase 2 ^ 64 duplos finitos distintos; desses, metade são positivos, e cerca de metade deles são <1,0.)

Mas você menciona geradores de números aleatórios: observe que um gerador de números aleatórios gerando números entre 0,0 e 1,0 não pode em geral produzir todos esses números; normalmente, ele produzirá apenas números na forma n / 2 ^ 53 com n um inteiro (consulte, por exemplo, a documentação Java para nextDouble ). Portanto, geralmente há apenas cerca de 2 ^ 53 (+/- 1, dependendo de quais terminais estão incluídos) valores possíveis para a random()saída. Isso significa que a maioria dos duplos em [0,0, 1,0] nunca será gerado.

Mark Dickinson
fonte
3

O artigo Nova matemática do Java, Parte 2: Números de ponto flutuante da IBM oferece o seguinte trecho de código para resolver isso (em flutuações, mas suspeito que funcione para duplos também):

public class FloatCounter {

    public static void main(String[] args) {
        float x = 1.0F;
        int numFloats = 0;
        while (x <= 2.0) {
            numFloats++;
            System.out.println(x);
            x = Math.nextUp(x);
        }
        System.out.println(numFloats);
    }
}

Eles têm este comentário sobre isso:

Acontece que existem exatamente 8.388.609 flutuadores entre 1,0 e 2,0 inclusive; grande, mas dificilmente a infinidade incontável de números reais que existem nesta faixa. Os números sucessivos estão separados por cerca de 0,0000001. Essa distância é chamada de ULP para unidade de menor precisão ou unidade em último lugar.

Mark Rushakoff
fonte
Sim, mas isso é para float, não double - floats tem valor da fração de 23 bits', então 2**23 -> 8388608diferentes valores entre poderes adjacentes de dois (a parte 'inclusivo' significa, naturalmente, você tem que contar mais um, a próxima potência de dois). doubles têm frações de 52 bits!
Alex Martelli
1
@Alex: Acho que terei que deixar o programa (modificado para duplas) em execução até o final do universo antes de obter os resultados ... :(
Mark Rushakoff
1
Eu me sinto estúpido; Acabei de escrever o doubleequivalente e pensei "Ei, vou responder minha própria pergunta em cerca de 5 minutos ..."
poligenelubrificantes
1
@polygene: Parece um problema do Projeto Euler em que a abordagem óbvia é inviável de calcular, mas deve haver uma fórmula simples e brilhante para resolver para o caso arbitrário ...
Mark Rushakoff
2
talvez não com um supercomputador verdadeiramente supercarregado: em uma máquina que leva apenas um nanossegundo para executar o loop interno, contar com doublepotências adjacentes de dois levaria cerca de 52 dias (é printlnclaro que seria muito improvável que funcionasse tão rápido, não importa o quê, vamos supor que uma instrução desapareça ;-). Acho que é viável levar um ano ou menos em uma máquina poderosa, mas realista ;-).
Alex Martelli
2
  1. 2 ^ 53 - o tamanho do significando / mantissa de um número de ponto flutuante de 64 bits incluindo o bit oculto.
  2. Aproximadamente sim, pois o sifnificand é fixo, mas o expoente muda.

Veja o artigo da Wikipedia para mais informações.

Yann Ramin
fonte
Sua resposta para 2 contradiz como eu entendo o funcionamento do FP.
poligenelubrificantes
Eu acho que 1é errado, porque o pouco escondida é sempre um - portanto, 2^52, não 2^53 distintos valores (entre os poderes adjacentes de dois, um incluído e o próximo excluídos - não ! Entre 0,0 e 1,0).
Alex Martelli
1

O duplo Java é um número binário64 IEEE 754.

Isso significa que precisamos considerar:

  1. Mantissa é de 52 bits
  2. O expoente é um número de 11 bits com tendência de 1023 (ou seja, com 1023 adicionado a ele)
  3. Se o expoente for todo 0 e a mantissa for diferente de zero então o número é dito não normalizado

Isso basicamente significa que há um total de 2 ^ 62-2 ^ 52 + 1 de possíveis representações duplas que, de acordo com o padrão, estão entre 0 e 1. Observe que 2 ^ 52 + 1 é para remover os casos do não normalizado números.

Lembre-se de que se a mantissa for positiva, mas o expoente for negativo, o número é positivo, mas menor que 1 :-)

Para outros números é um pouco mais difícil porque os números inteiros da borda podem não ser representados de maneira precisa na representação IEEE 754, e porque existem outros bits usados ​​no expoente para poder representar os números, portanto, quanto maior o número, menor os diferentes valores.

njsf
fonte