JavaRush /Java Blog /Random-ID /Perangkat bilangan real

Perangkat bilangan real

Dipublikasikan di grup Random-ID
Halo! Pada kuliah hari ini kita akan membahas tentang bilangan di Java, khususnya bilangan real. Perangkat bilangan real - 1Jangan panik! :) Tidak akan ada kesulitan matematika dalam perkuliahan. Kami akan berbicara tentang bilangan real secara eksklusif dari sudut pandang “programmer” kami. Jadi, apa itu “bilangan real”? Bilangan real adalah bilangan yang mempunyai bagian pecahan (yang bisa bernilai nol). Mereka bisa positif atau negatif. Berikut beberapa contohnya: 15 56,22 0,0 1242342343445246 -232336.11 Bagaimana cara kerja bilangan real? Sederhana saja: terdiri dari bagian bilangan bulat, bagian pecahan, dan tanda. Untuk bilangan positif tandanya biasanya tidak ditunjukkan secara eksplisit, tetapi untuk bilangan negatif ditunjukkan. Sebelumnya, kami telah memeriksa secara detail operasi angka apa saja yang dapat dilakukan di Java. Diantaranya terdapat banyak operasi matematika standar - penjumlahan, pengurangan, dll. Ada juga beberapa operasi baru untuk Anda: misalnya, sisa pembagian. Tapi bagaimana sebenarnya cara kerja angka di dalam komputer? Dalam bentuk apa mereka disimpan dalam memori?

Menyimpan bilangan real dalam memori

Saya pikir ini bukan sebuah penemuan bagi Anda bahwa angka bisa besar dan kecil :) Mereka dapat dibandingkan satu sama lain. Misalnya angka 100 lebih kecil dari angka 423324. Apakah ini mempengaruhi pengoperasian komputer dan program kita? Sebenarnya ya . Setiap angka diwakili di Java dengan rentang nilai tertentu :
Jenis Ukuran memori (bit) Jarak nilai
byte 8 sedikit -128 hingga 127
short 16 sedikit -32768 hingga 32767
char 16 sedikit integer unsigned yang mewakili karakter UTF-16 (huruf dan angka)
int 32 bit dari -2147483648 hingga 2147483647
long 64 bit dari -9223372036854775808 hingga 9223372036854775807
float 32 bit dari 2 -149 hingga (2-2 -23 )*2 127
double 64 bit dari 2 -1074 hingga (2-2 -52 )*2 1023
Hari ini kita akan berbicara tentang dua tipe terakhir - floatdan double. Keduanya melakukan tugas yang sama - merepresentasikan bilangan pecahan. Mereka juga sering disebut “ angka floating point” . Ingat istilah ini untuk kedepannya :) Misalnya angka 2.3333 atau 134.1212121212. Cukup aneh. Lagi pula, ternyata tidak ada perbedaan antara kedua tipe ini, karena keduanya melakukan tugas yang sama? Tapi ada perbedaan. Perhatikan kolom “size in memory” pada tabel di atas. Semua angka (dan bukan hanya angka - semua informasi secara umum) disimpan dalam memori komputer dalam bentuk bit. Sedikit adalah unit informasi terkecil. Ini cukup sederhana. Setiap bit sama dengan 0 atau 1. Dan kata “ bit ” sendiri berasal dari bahasa Inggris “ binary digit ” - bilangan biner. Saya rasa Anda mungkin pernah mendengar tentang keberadaan sistem bilangan biner dalam matematika. Bilangan desimal apa pun yang kita kenal dapat direpresentasikan sebagai himpunan satu dan nol. Misalnya, angka 584.32 dalam biner akan terlihat seperti ini: 100100100001010001111 . Masing-masing angka satu dan nol adalah bit yang terpisah. Sekarang Anda harus lebih jelas tentang perbedaan antara tipe data. Misalnya, jika kita membuat sejumlah type float, kita hanya memiliki 32 bit yang dapat kita gunakan. Saat membuat nomor, floatini adalah jumlah ruang yang akan dialokasikan untuk nomor tersebut di memori komputer. Jika kita ingin membuat angka 123456789.65656565656565, dalam biner akan terlihat seperti ini: 11101011011110011010001010110101000000 . Terdiri dari 38 satu dan nol, sehingga diperlukan 38 bit untuk menyimpannya di memori. floatNomor ini tidak akan “cocok” dengan tipenya ! Oleh karena itu, angka 123456789 dapat direpresentasikan sebagai tipe double. Sebanyak 64 bit dialokasikan untuk menyimpannya: ini cocok untuk kita! Tentu saja kisaran nilainya juga akan sesuai. Untuk kenyamanan, Anda dapat menganggap angka sebagai kotak kecil berisi sel. Jika ada cukup sel untuk menyimpan setiap bit, maka tipe data dipilih dengan benar :) Perangkat bilangan real - 2Tentu saja, jumlah memori yang dialokasikan berbeda juga mempengaruhi nomor itu sendiri. Harap dicatat bahwa tipe floatmemiliki doublerentang nilai yang berbeda. Apa artinya ini dalam praktiknya? Suatu bilangan doubledapat menyatakan presisi yang lebih besar dibandingkan suatu bilangan float. Angka floating point 32-bit (di Java tipe ini persisnya float) memiliki presisi sekitar 24 bit, yaitu sekitar 7 tempat desimal. Dan angka 64-bit (di Java tipe ini double) memiliki presisi sekitar 53 bit, yaitu sekitar 16 tempat desimal. Berikut adalah contoh yang menunjukkan perbedaan ini dengan baik:
public class Main {

   public static void main(String[] args)  {

       float f = 0.0f;
       for (int i=1; i <= 7; i++) {
           f += 0.1111111111111111;
       }

       System.out.println(f);
   }
}
Hasilnya, apa yang harus kita dapatkan di sini? Tampaknya semuanya cukup sederhana. Kita mempunyai angka 0,0, dan kita menambahkan 0,1111111111111111 ke dalamnya sebanyak 7 kali berturut-turut. Hasilnya seharusnya 0,7777777777777777. Tapi kami membuat nomor float. Ukurannya dibatasi hingga 32 bit dan, seperti yang kami katakan sebelumnya, ia mampu menampilkan angka hingga sekitar 7 desimal. Oleh karena itu, pada akhirnya hasil yang kita dapatkan di konsol akan berbeda dari yang kita harapkan:

0.7777778
Nomornya sepertinya “terputus”. Anda sudah mengetahui bagaimana data disimpan dalam memori - dalam bentuk bit, jadi hal ini tidak akan mengejutkan Anda. Jelas mengapa hal ini terjadi: hasilnya 0.7777777777777777 tidak sesuai dengan 32 bit yang dialokasikan untuk kita, jadi dipotong agar sesuai dengan variabel tipe float:) Kita dapat mengubah tipe variabel doubledalam contoh kita, dan kemudian yang terakhir hasilnya tidak akan terpotong:
public class Main {

   public static void main(String[] args)  {

       double f = 0.0;
       for (int i=1; i <= 7; i++) {
           f += 0.1111111111111111;
       }

       System.out.println(f);
   }
}

0.7777777777777779
Sudah ada 16 tempat desimal, hasilnya “muat” menjadi 64 bit. Ngomong-ngomong, mungkin Anda memperhatikan bahwa dalam kedua kasus tersebut, hasilnya tidak sepenuhnya benar? Perhitungan dilakukan dengan kesalahan kecil. Kami akan membicarakan alasannya di bawah ini :) Sekarang mari kita jelaskan beberapa kata tentang bagaimana Anda dapat membandingkan angka satu sama lain.

Perbandingan bilangan real

Kami sebagian telah menyinggung masalah ini pada kuliah terakhir, ketika kami berbicara tentang operasi perbandingan. Kami tidak akan menganalisis ulang operasi seperti >, <, >=. <=Mari kita lihat contoh yang lebih menarik:
public class Main {

   public static void main(String[] args)  {

       double f = 0.0;
       for (int i=1; i <= 10; i++) {
           f += 0.1;
       }

       System.out.println(f);
   }
}
Menurut Anda nomor berapa yang akan ditampilkan di layar? Jawaban logisnya adalah jawabannya: angka 1. Kita mulai menghitung dari angka 0,0 dan berturut-turut menambahkan 0,1 sepuluh kali berturut-turut. Segalanya tampak benar, seharusnya menjadi satu. Coba jalankan kode ini, dan jawabannya akan sangat mengejutkan Anda :) Output konsol:

0.9999999999999999
Tetapi mengapa kesalahan terjadi pada contoh sederhana seperti itu? O_o Di sini bahkan siswa kelas lima dapat dengan mudah menjawab dengan benar, tetapi program Java memberikan hasil yang tidak akurat. “Tidak akurat” adalah kata yang lebih baik di sini daripada “salah.” Kami masih mendapatkan angka yang sangat dekat dengan satu, dan bukan hanya nilai acak :) Ini berbeda dari nilai yang benar hanya satu milimeter. Tapi kenapa? Mungkin ini hanya kesalahan satu kali saja. Mungkinkah komputernya rusak? Mari kita coba menulis contoh lain.
public class Main {

   public static void main(String[] args)  {

       //add 0.1 to zero eleven times in a row
       double f1 = 0.0;
       for (int i = 1; i <= 11; i++) {
           f1 += .1;
       }

       // Multiply 0.1 by 11
       double f2 = 0.1 * 11;

       //should be the same - 1.1 in both cases
       System.out.println("f1 = " + f1);
       System.out.println("f2 = " + f2);

       // Let's check!
       if (f1 == f2)
           System.out.println("f1 and f2 are equal!");
       else
           System.out.println("f1 and f2 are not equal!");
   }
}
Keluaran konsol:

f1 = 1.0999999999999999
f2 = 1.1
f1 и f2 не равны!
Jadi, ini jelas bukan masalah gangguan komputer :) Apa yang terjadi? Kesalahan seperti ini terkait dengan cara angka direpresentasikan dalam bentuk biner di memori komputer. Faktanya adalah bahwa dalam sistem biner tidak mungkin untuk merepresentasikan angka secara akurat 0,1 . Omong-omong, sistem desimal juga memiliki masalah serupa: tidak mungkin merepresentasikan pecahan dengan benar (dan alih-alih ⅓ kita mendapatkan 0,33333333333333..., yang juga bukan hasil yang tepat). Tampaknya sepele: dengan perhitungan seperti itu, perbedaannya bisa seperseratus ribu bagian (0,00001) atau bahkan kurang. Namun bagaimana jika keseluruhan hasil Program Sangat Serius Anda bergantung pada perbandingan ini?
if (f1 == f2)
   System.out.println("Rocket flies into space");
else
   System.out.println("The launch is canceled, everyone goes home");
Kami jelas mengharapkan kedua angka tersebut sama, namun karena desain memori internal, kami membatalkan peluncuran roket. Perangkat bilangan real - 3Kalau iya, kita perlu memutuskan bagaimana cara membandingkan dua bilangan floating-point agar hasil perbandingannya lebih... ummm... bisa ditebak. Jadi, kita telah mempelajari aturan No. 1 saat membandingkan bilangan real: jangan pernah menggunakan ==bilangan floating point saat membandingkan bilangan real. Oke, menurut saya itu sudah cukup contoh buruknya :) Mari kita lihat contoh yang baik!
public class Main {

   public static void main(String[] args)  {

       final double threshold = 0.0001;

       //add 0.1 to zero eleven times in a row
       double f1 = .0;
       for (int i = 1; i <= 11; i++) {
           f1 += .1;
       }

       // Multiply 0.1 by 11
       double f2 = .1 * 11;

       System.out.println("f1 = " + f1);
       System.out.println("f2 = " + f2);

       if (Math.abs(f1 - f2) < threshold)
           System.out.println("f1 and f2 are equal");
       else
           System.out.println("f1 and f2 are not equal");
   }
}
Di sini kita pada dasarnya melakukan hal yang sama, namun mengubah cara kita membandingkan angka. Kami memiliki angka "ambang batas" khusus - 0,0001, sepersepuluh ribu. Ini mungkin berbeda. Hal ini bergantung pada seberapa tepat perbandingan yang Anda perlukan dalam kasus tertentu. Anda bisa membuatnya lebih besar atau lebih kecil. Dengan menggunakan metode ini, Math.abs()kita memperoleh modulus suatu bilangan. Modulus adalah nilai suatu bilangan, apa pun tandanya. Misalnya bilangan -5 dan 5 akan mempunyai modulus yang sama dan sama dengan 5. Kita kurangi bilangan kedua dari bilangan pertama, dan jika hasil yang dihasilkan, apapun tandanya, lebih kecil dari ambang batas yang kita tetapkan, maka jumlah kita sama. Bagaimanapun, angka tersebut sama dengan tingkat akurasi yang kami tetapkan menggunakan “angka ambang batas” kami , yaitu, minimal sama dengan sepersepuluh ribu. Metode perbandingan ini akan menyelamatkan Anda dari perilaku tak terduga yang kita lihat dalam kasus ==. Cara lain yang baik untuk membandingkan bilangan real adalah dengan menggunakan kelas khusus BigDecimal. Kelas ini khusus dibuat untuk menyimpan bilangan yang sangat besar dengan bagian pecahan. Berbeda dengan doubledan float, ketika menggunakan BigDecimalpenjumlahan, pengurangan dan operasi matematika lainnya dilakukan tidak menggunakan operator ( +-, dll), tetapi menggunakan metode. Inilah yang akan terlihat dalam kasus kami:
import java.math.BigDecimal;

public class Main {

   public static void main(String[] args)  {

       /*Create two BigDecimal objects - zero and 0.1.
       We do the same thing as before - add 0.1 to zero 11 times in a row
       In the BigDecimal class, addition is done using the add () method */
       BigDecimal f1 = new BigDecimal(0.0);
       BigDecimal pointOne = new BigDecimal(0.1);
       for (int i = 1; i <= 11; i++) {
           f1 = f1.add(pointOne);
       }

       /*Nothing has changed here either: create two BigDecimal objects
       and multiply 0.1 by 11
       In the BigDecimal class, multiplication is done using the multiply() method*/
       BigDecimal f2 = new BigDecimal(0.1);
       BigDecimal eleven = new BigDecimal(11);
       f2 = f2.multiply(eleven);

       System.out.println("f1 = " + f1);
       System.out.println("f2 = " + f2);

       /*Another feature of BigDecimal is that number objects need to be compared with each other
       using the special compareTo() method*/
       if (f1.compareTo(f2) == 0)
           System.out.println("f1 and f2 are equal");
       else
           System.out.println("f1 and f2 are not equal");
   }
}
Output konsol seperti apa yang akan kita dapatkan?

f1 = 1.1000000000000000610622663543836097232997417449951171875
f2 = 1.1000000000000000610622663543836097232997417449951171875
f1 и f2 равны
Kami mendapatkan hasil yang kami harapkan. Dan perhatikan seberapa akurat angka-angka kita, dan berapa banyak angka desimal yang bisa dimasukkan ke dalamnya! Lebih dari pada di floatdan bahkan di double! Ingat kelasnya BigDecimaluntuk kedepannya, kamu pasti membutuhkannya :) Fiuh! Ceramahnya cukup panjang, tapi Anda berhasil: bagus sekali! :) Sampai jumpa di pelajaran berikutnya, programmer masa depan!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION