Ada berapa angka ganda antara 0,0 dan 1,0?

95

Ini adalah sesuatu yang ada di pikiran saya selama bertahun-tahun, tetapi saya tidak pernah meluangkan waktu untuk bertanya sebelumnya.

Banyak (pseudo) generator nomor acak menghasilkan nomor acak antara 0,0 dan 1,0. Secara matematis ada bilangan tak hingga dalam rentang ini, tetapi doublemerupakan bilangan floating point, dan oleh karena itu memiliki presisi hingga.

Jadi pertanyaannya adalah:

  1. Berapa banyak doubleangka antara 0,0 dan 1,0?
  2. Apakah ada banyak angka antara 1 dan 2? Antara 100 dan 101? Antara 10 ^ 100 dan 10 ^ 100 + 1?

Catatan: jika itu membuat perbedaan, saya tertarik pada definisi Java doublesecara khusus.

poligenelubricants
sumber

Jawaban:

68

Java doubledalam format IEEE-754 , oleh karena itu mereka memiliki pecahan 52-bit; antara dua pangkat dua yang berdekatan (termasuk satu dan tidak termasuk pangkat berikutnya), oleh karena itu akan ada 2 pangkat 52 yang berbeda double(yaitu, 4503599627370496 di antaranya). Misalnya, itu adalah jumlah perbedaan doubleantara 0,5 yang disertakan dan 1,0 yang dikecualikan, dan banyak juga yang berada di antara 1,0 yang disertakan dan 2,0 yang dikecualikan, dan seterusnya.

Menghitung doublesantara 0,0 dan 1,0 lebih sulit daripada melakukannya di antara pangkat dua, karena ada banyak pangkat dua yang termasuk dalam kisaran itu, dan, juga, seseorang akan mengalami masalah pelik tentang bilangan yang dinormalisasi. 10 dari 11 bit eksponen mencakup kisaran yang dimaksud, jadi, termasuk angka yang didenormalisasi (dan saya pikir beberapa jenis NaN) Anda akan memiliki 1024 kali doubles seperti yang berada di antara pangkat dua - tidak lebih dari 2**62totalnya . Tidak termasuk dinormalisasi & c, saya yakin jumlahnya akan menjadi 1023 kali 2**52.

Untuk rentang arbitrer seperti "100 hingga 100.1", ini lebih sulit karena batas atas tidak dapat secara tepat direpresentasikan sebagai double(bukan kelipatan tepat dari pangkat dua). Sebagai perkiraan praktis, karena progresi antara pangkat dua adalah linier, Anda dapat mengatakan bahwa rentang tersebut adalah 0.1 / 64rentang antara pangkat dua di sekitarnya (64 dan 128), jadi Anda akan mengharapkan

(0.1 / 64) * 2**52

berbeda doubles - yang datang ke 7036874417766.4004... memberi atau menerima satu atau dua ;-).

Alex Martelli
sumber
@Alex: sekedar catatan, ketika saya menulis 100 hingga 100,1 saya salah tulis. Maksud saya 100 sampai 101. Pada dasarnya, antara N dan N + 1 untuk sembarang N.
polygenelubricants
4
@ Alex: jadi biarkan saya 2**64menjelaskan ini: tidak ada lebih dari kemungkinan nilai ganda (karena ini adalah tipe 64 bit), dan tampaknya proporsi BESAR dari nilai-nilai itu ada di antara 0..1?
poligenelubricants
9
@ poligena, ya dan ya - khususnya, sekitar seperempat dari nilai yang mungkin (untuk representasi floating point "normal" dari setiap panjang basis dan eksponen vs pecahan) terletak antara 0,0 dan 1,0 (seperempat lain antara 1,0 dan tak terhingga, dan setengah tersisa pada setengah negatif dari sumbu nyata). Pada dasarnya, separuh nilai eksponen (dengan bias normal, separuh dalam rentangnya) mewakili pangkat negatif dari basis, oleh karena itu angka <1,0.
Alex Martelli
8
@polygenelubricants: untuk banyak aplikasi, kisaran antara 0 dan 1 jauh lebih penting dan menarik daripada kisaran antara 100 dan 101, itulah mengapa ia mendapat bagian nilai yang lebih besar. Misalnya, dalam fisika, Anda sering harus berurusan dengan nilai-nilai yang sangat kecil seperti konstanta gravitasi Newton pada 6,67e-11. Memiliki presisi yang baik lebih bermanfaat daripada antara 100 dan 101. Baca floating-point-gui.de untuk informasi lebih lanjut.
Michael Borgwardt
1
Anda juga dapat menskalakan angka apa pun ke antara 0,0 dan 1,0, melacak skala secara terpisah, menghasilkan lebih sedikit kesalahan dalam komputasi. Sangat menyenangkan bila seluruh garis bilangan dapat dipetakan di antara dua bilangan!
codekaizen
44

Setiap doublenilai yang representasi berada di antara 0x0000000000000000dan 0x3ff0000000000000terletak di interval [0,0, 1,0]. Itu (2 ^ 62 - 2 ^ 52) nilai yang berbeda (plus atau minus pasangan tergantung pada apakah Anda menghitung titik akhir).

Interval [1.0, 2.0] sesuai dengan representasi antara 0x3ff0000000000000dan 0x400000000000000; itu 2 ^ 52 nilai yang berbeda.

Interval [100.0, 101.0] sesuai dengan representasi antara 0x4059000000000000dan 0x4059400000000000; itu 2 ^ 46 nilai yang berbeda.

Tidak ada penggandaan antara 10 ^ 100 dan 10 ^ 100 + 1 . Tidak ada satu pun dari angka-angka itu yang dapat diwakili dalam presisi ganda, dan tidak ada angka ganda yang berada di antara keduanya. Dua angka presisi ganda terdekat adalah:

99999999999999982163600188718701095...

dan

10000000000000000159028911097599180...
Stephen Canon
sumber
+1, untuk jawaban tepat yang didukung dengan baik. (Jika Anda pilih-pilih dalam menghitung titik akhir, ingatlah bahwa +0.0 dan -0.0 memiliki representasi yang berbeda.)
Jim Lewis
1
+1, akhir yang berliku-liku! Merasa seperti saya sedang membaca naskah M. Night Shyamalan!
poligenelubricants
7

Orang lain telah menjelaskan bahwa ada sekitar 2 ^ 62 ganda dalam kisaran [0,0, 1,0].
(Tidak benar-benar mengejutkan: ada hampir 2 ^ 64 ganda terbatas yang berbeda; dari mereka, setengah positif, dan kira-kira setengah dari orang-orang yang <1.0.)

Tetapi Anda menyebutkan generator angka acak: perhatikan bahwa generator angka acak yang menghasilkan angka antara 0,0 dan 1,0 secara umum tidak dapat menghasilkan semua angka ini; biasanya itu hanya akan menghasilkan angka dalam bentuk n / 2 ^ 53 dengan n sebuah integer (lihat misalnya dokumentasi Java untuk nextDouble ). Jadi biasanya hanya ada sekitar 2 ^ 53 (+/- 1, tergantung pada titik akhir mana yang disertakan) kemungkinan nilai untuk random()keluaran. Ini berarti bahwa sebagian besar penggandaan di [0,0, 1,0] tidak akan pernah dihasilkan.

Mark Dickinson
sumber
3

Artikel matematika baru Java, Bagian 2: Angka floating-point dari IBM menawarkan potongan kode berikut untuk menyelesaikan ini (dalam float, tetapi saya curiga itu berfungsi untuk ganda juga):

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

Mereka punya komentar tentang itu:

Ternyata tepat ada 8.388.609 float antara 1.0 dan 2.0 inklusif; besar tetapi hampir tidak terhitung tak terhingga dari bilangan real yang ada dalam kisaran ini. Bilangan berturut-turut berjarak sekitar 0,0000001. Jarak ini disebut ULP untuk satuan dengan presisi paling rendah atau satuan di tempat terakhir.

Mark Rushakoff
sumber
Ya, tapi itu untuk float, not double - floats memiliki nilai pecahan 23 bit, jadi 2**23 -> 8388608nilai yang berbeda antara pangkat dua yang berdekatan (bagian "inklusif" tentu saja berarti Anda harus menghitung satu lagi, pangkat dua berikutnya). doubles memiliki pecahan 52-bit!
Alex Martelli
1
@Alex: Saya kira saya harus membiarkan program (dimodifikasi untuk ganda) berjalan sampai akhir alam semesta atau lebih sebelum saya bisa mendapatkan hasilnya ... :(
Mark Rushakoff
1
Saya merasa bodoh; Saya baru saja menulis doublepadanannya dan berpikir "Hei, saya akan menjawab pertanyaan saya sendiri dalam waktu sekitar 5 menit ..."
polygenelubricants
1
@polygene: Ini terasa seperti masalah Project Euler di mana pendekatan yang jelas tidak layak untuk dihitung, tetapi harus ada beberapa rumus sederhana yang brilian untuk menyelesaikan kasus arbitrer ...
Mark Rushakoff
2
mungkin tidak dengan superkomputer yang benar-benar supercharged: pada mesin yang hanya membutuhkan satu nanodetik untuk menjalankan loop dalam, menghitung dengan doubledua kekuatan yang berdekatan akan memakan waktu sekitar 52 hari ( printlntentu saja akan sangat tidak mungkin untuk berjalan secepat itu apa pun yang terjadi, jadi mari kita asumsikan bahwa satu pernyataan hilang ;-). Saya pikir itu layak untuk mengambil satu tahun atau kurang pada mesin yang kuat tapi realistis ;-).
Alex Martelli
2
  1. 2 ^ 53 - ukuran signifikansi / mantissa dari angka floating point 64bit termasuk bit tersembunyi.
  2. Secara kasar ya, karena sifnificand tetap tetapi eksponennya berubah.

Lihat artikel wikipedia untuk informasi lebih lanjut.

Yann Ramin
sumber
Jawaban Anda untuk 2 kontradiksi bagaimana saya memahami kerja FP.
poligenelubricants
Saya pikir 1ini salah karena agak tersembunyi selalu satu - karena itu, 2^52, tidak 2^53 berbeda nilai-nilai (antara kekuatan yang berdekatan dari dua, satu disertakan dan yang berikutnya dikecualikan - tidak ! Antara 0,0 dan 1,0).
Alex Martelli
1

Ganda Java adalah nomor binary64 IEEE 754.

Artinya kita perlu mempertimbangkan:

  1. Mantissa 52 bit
  2. Eksponen adalah angka 11 bit dengan bias 1023 (yaitu dengan 1023 ditambahkan ke dalamnya)
  3. Jika eksponen semuanya 0 dan mantisa bukan nol maka bilangan tersebut dikatakan tidak dinormalisasi

Ini pada dasarnya berarti ada total 2 ^ 62-2 ^ 52 + 1 kemungkinan representasi ganda yang menurut standar adalah antara 0 dan 1. Perhatikan bahwa 2 ^ 52 + 1 adalah untuk menghapus kasus non-normalisasi nomor.

Ingatlah bahwa jika mantissa positif tetapi eksponen negatif, bilangan positif tetapi kurang dari 1 :-)

Untuk bilangan lain sedikit lebih sulit karena bilangan bulat tepi mungkin tidak dapat direpresentasikan secara tepat dalam representasi IEEE 754, dan karena ada bit lain yang digunakan dalam eksponen untuk dapat mewakili bilangan tersebut, jadi semakin besar angkanya semakin rendah nilai-nilai yang berbeda.

njsf
sumber