Quanti numeri doppi ci sono tra 0,0 e 1,0?


93

Questo è qualcosa che ho in mente da anni, ma non ho mai avuto il tempo di chiederlo prima.

Molti (pseudo) generatori di numeri casuali generano un numero casuale compreso tra 0,0 e 1,0. Matematicamente ci sono numeri infiniti in questo intervallo, ma doubleè un numero in virgola mobile e quindi ha una precisione finita.

Quindi le domande sono:

  1. Quanti doublenumeri ci sono tra 0,0 e 1,0?
  2. Ci sono altrettanti numeri tra 1 e 2? Tra 100 e 101? Tra 10 ^ 100 e 10 ^ 100 + 1?

Nota: se fa la differenza, mi interessa in particolare la definizione di Java double.

Risposte:


68

I Java doublesono in formato IEEE-754 , quindi hanno una frazione di 52 bit; tra due potenze adiacenti di due (inclusa una ed esclusiva della successiva), ci saranno quindi da 2 alla 52a potenza differenti doubles (cioè 4503599627370496 di esse). Ad esempio, questo è il numero di messaggi distinti doublecompreso tra 0,5 incluso e 1,0 escluso, ed esattamente che molti si trovano anche tra 1,0 incluso e 2,0 escluso e così via.

Contare doublestra 0,0 e 1,0 è più difficile che farlo tra potenze di due, perché ci sono molte potenze di due incluse in quell'intervallo e, inoltre, si entra nelle spinose questioni dei numeri denormalizzati. 10 degli 11 bit degli esponenti coprono l'intervallo in questione, quindi, inclusi i numeri denormalizzati (e penso alcuni tipi di NaN) avresti 1024 volte la doubles come distesa tra potenze di due - non più del 2**62totale comunque . Escludendo denormalizzato & c, credo che il conteggio sarebbe 1023 volte 2**52.

Per un intervallo arbitrario come "da 100 a 100,1" è ancora più difficile perché il limite superiore non può essere rappresentato esattamente come a double(non essendo un multiplo esatto di qualsiasi potenza di due). Come comoda approssimazione, poiché la progressione tra le potenze di due è lineare, si potrebbe dire che tale intervallo è 0.1 / 64la distanza tra le potenze circostanti di due (64 e 128), quindi ti aspetteresti che

(0.1 / 64) * 2**52

distinta doubles - che viene a 7036874417766.4004... dare o prendere uno o due ;-).


@ Alex: solo per notare, quando ho scritto da 100 a 100,1 ho scritto male. Intendevo da 100 a 101. Fondamentalmente, tra N e N + 1 per N. arbitrario
lubrificanti poligenici

4
@ Alex: quindi fammi capire bene: non possono esserci più di 2**64possibili valori doppi (poiché è un tipo a 64 bit), e apparentemente una proporzione ENORME di quei valori si trova tra 0..1?
lubrificanti poligenici

9
@polygene, yes e yes - in particolare, circa un quarto dei valori possibili (per qualsiasi rappresentazione in virgola mobile "normale" di qualsiasi base ed esponente rispetto alla lunghezza della frazione) si trova tra 0,0 e 1,0 (un altro quarto tra 1,0 e infinito, e il rimanente metà sulla metà negativa dell'asse reale). In sostanza, metà dei valori dell'esponente (con una polarizzazione normale, a metà del suo intervallo) rappresentano potenze negative della base, quindi numeri <1.0.
Alex Martelli

8
@polygenelubricants: per molte applicazioni, l'intervallo tra 0 e 1 è molto, molto più importante e interessante dell'intervallo tra 100 e 101, ecco perché ottiene una quota maggiore dei valori. Ad esempio, in fisica, devi spesso avere a che fare con valori ridicolmente piccoli come la costante graviational di Newton a 6.67e-11. Avere una buona precisione è più utile che tra 100 e 101. Leggi floating-point-gui.de per maggiori informazioni.
Michael Borgwardt

1
Puoi anche scalare qualsiasi numero tra 0,0 e 1,0, tenendo traccia della scala separatamente, producendo meno errori nel calcolo. È bello quando l'intera linea numerica può essere mappata tra due numeri!
codekaizen

42

Ogni doublevalore la cui rappresentazione è compresa tra 0x0000000000000000e si 0x3ff0000000000000trova nell'intervallo [0,0, 1,0]. Sono (2 ^ 62 - 2 ^ 52) valori distinti (più o meno una coppia a seconda che si contino gli endpoint).

L'intervallo [1.0, 2.0] corrisponde alle rappresentazioni tra 0x3ff0000000000000e 0x400000000000000; questo è 2 ^ 52 valori distinti.

L'intervallo [100,0, 101,0] corrisponde alle rappresentazioni tra 0x4059000000000000e 0x4059400000000000; questo è 2 ^ 46 valori distinti.

Non ci sono doppi tra 10 ^ 100 e 10 ^ 100 + 1 . Nessuno di questi numeri è rappresentabile con doppia precisione e non ci sono doppi che cadono tra di loro. I due numeri a doppia precisione più vicini sono:

99999999999999982163600188718701095...

e

10000000000000000159028911097599180...

+1, per una risposta esatta ben supportata. (Se sei pignolo nel conteggio degli endpoint, ricorda che +0.0 e -0.0 hanno rappresentazioni distinte.)
Jim Lewis,

1
+1, una tale svolta! Mi sembrava di leggere una sceneggiatura di M. Night Shyamalan!
lubrificanti poligenici il

7

Altri hanno già spiegato che ci sono circa 2 ^ 62 doppi nell'intervallo [0,0, 1,0].
(Non è davvero sorprendente: ci sono quasi 2 ^ 64 doppi finiti distinti; di questi, la metà sono positivi e circa la metà di questi sono <1,0.)

Ma lei menziona i generatori di numeri casuali: nota che un generatore di numeri casuali che genera numeri compresi tra 0,0 e 1,0 non può in generale produrre tutti questi numeri; tipicamente produrrà solo numeri nella forma n / 2 ^ 53 con n un numero intero (vedere ad esempio la documentazione Java per nextDouble ). Quindi di solito ci sono solo circa 2 ^ 53 (+/- 1, a seconda di quali endpoint sono inclusi) valori possibili per l' random()output. Ciò significa che la maggior parte dei doppi in [0.0, 1.0] non verrà mai generata.


3

L'articolo Java's new math, Part 2: Floating-point numbers from IBM offre il seguente frammento di codice per risolvere questo problema (in float, ma sospetto che funzioni anche per i doppi):

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);
    }
}

Hanno questo commento al riguardo:

Risulta che ci sono esattamente 8.388.609 float tra 1.0 e 2.0 inclusi; grande ma difficilmente l'infinità innumerevole di numeri reali che esistono in questo intervallo. I numeri successivi sono distanti circa 0,0000001. Questa distanza è chiamata ULP per unità di minima precisione o unità all'ultimo posto.


Sì, ma questo è perché float, no double - floats hanno un valore di frazione di 23 bit, quindi 2**23 -> 8388608valori diversi tra potenze adiacenti di due (la parte "inclusiva" ovviamente significa che devi contarne uno in più, la potenza successiva di due). doublehanno frazioni di 52 bit!
Alex Martelli

1
@Alex: Immagino che dovrò lasciare il programma (modificato per il doppio) in esecuzione fino alla fine dell'universo o giù di lì prima di poter ottenere i risultati ... :(
Mark Rushakoff

1
Mi sento stupido; Ho appena scritto l' doubleequivalente e ho pensato "Ehi, risponderò alla mia stessa domanda in circa 5 minuti ..."
lubrificanti poligenici

1
@polygene: Sembra un problema del Progetto Eulero in cui l'approccio ovvio non è fattibile da calcolare, ma deve esserci una formula brillantemente semplice da risolvere per il caso arbitrario ...
Mark Rushakoff

2
forse non con un supercomputer veramente sovralimentato: su una macchina che impiega solo un nanosecondo per eseguire il ciclo interno, il conteggio doubletra potenze adiacenti di due richiederebbe circa 52 giorni ( printlnovviamente sarebbe molto improbabile che funzioni così velocemente, non importa cosa, quindi supponiamo che un'istruzione scompaia ;-). Penso che sia fattibile impiegare un anno o meno su una macchina potente ma realistica ;-).
Alex Martelli

2
  1. 2 ^ 53 - la dimensione del significante / mantissa di un numero in virgola mobile a 64 bit incluso il bit nascosto.
  2. Più o meno sì, poiché il sifnificando è fisso ma l'esponente cambia.

Vedere l' articolo di wikipedia per ulteriori informazioni.


La tua risposta per 2 contraddice il modo in cui intendo il funzionamento di FP.
lubrificanti poligenici

Penso che 1è sbagliato perché il bit nascosto è sempre uno - quindi 2^52, non 2^53 distinti valori (tra i poteri adiacenti di due, uno in dotazione e la prossima esclusa - non ! Tra 0,0 e 1,0).
Alex Martelli

1

Il Java double è un numero binario 64 IEEE 754.

Ciò significa che dobbiamo considerare:

  1. Mantissa è a 52 bit
  2. L'esponente è un numero di 11 bit con 1023 bias (cioè con 1023 aggiunto)
  3. Se l'esponente è tutto 0 e la mantissa è diversa da zero, si dice che il numero non è normalizzato

Questo in pratica significa che c'è un totale di 2 ^ 62-2 ^ 52 + 1 di possibili doppie rappresentazioni che secondo lo standard sono comprese tra 0 e 1. Si noti che 2 ^ 52 + 1 sta per rimuovere i casi dei non normalizzati numeri.

Ricorda che se la mantissa è positiva ma l'esponente è negativo il numero è positivo ma inferiore a 1 :-)

Per altri numeri è un po 'più difficile perché i numeri interi sul bordo potrebbero non essere rappresentabili in modo preciso nella rappresentazione IEEE 754 e perché ci sono altri bit usati nell'esponente per poter rappresentare i numeri, quindi maggiore è il numero minore è i diversi valori.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.