Tipe dan kind intrinsik

Tipe intrinsik integer, real, logical, dan character beserta kind literal seperti real32 dan real64.

Daftar isi

Topik ini membahas empat tipe intrinsik Fortran, yaitu integer, real, logical, dan character, beserta konsep kind yang memilih presisi sebuah tipe. Konstanta bernama real32 dan real64 dari modul iso_fortran_env diperkenalkan sebagai cara memilih presisi secara portabel. Kehilangan presisi yang dialami tipe real default ditunjukkan dengan program yang dapat dijalankan, sehingga perbedaan hasil pada real32 dan real64 dapat diamati langsung dari keluarannya.

Empat tipe intrinsik#

Tipe intrinsik (intrinsic type) adalah tipe yang sudah disediakan bahasa tanpa perlu didefinisikan ulang. Keempatnya mencakup bilangan bulat (integer), bilangan pecahan (real), nilai kebenaran (logical), dan teks (character). Setiap variabel dideklarasikan dengan menuliskan nama tipe, tanda titik dua ganda ::, lalu daftar nama variabel, dan setiap variabel baru diberi nilai lewat penugasan sebelum digunakan.

fortran tipe.f90
program tipe
  use, intrinsic :: iso_fortran_env, only : real32, real64
  implicit none

  integer :: jumlah
  real :: suhu
  logical :: valid
  character(len=12) :: nama
  real(real32) :: x32
  real(real64) :: x64

  jumlah = 42
  suhu = 30.5
  valid = .true.
  nama = "Fortran"
  x32 = 1.0_real32
  x64 = 1.0_real64

  print *, "integer   :", jumlah
  print *, "real      :", suhu
  print *, "logical   :", valid
  print *, "character : " // trim(nama)
  print *, "kind integer default:", kind(jumlah)
  print *, "kind real default   :", kind(suhu)
  print *, "kind real32         :", kind(x32)
  print *, "kind real64         :", kind(x64)
end program tipe
bash
# kompilasi program tipe menjadi berkas executable bernama tipe
gfortran tipe.f90 -o tipe
# jalankan program hasil kompilasi
./tipe
text Keluaran
 integer   :          42
 real      :   30.5000000
 logical   : T
 character : Fortran
 kind integer default:           4
 kind real default   :           4
 kind real32         :           4
 kind real64         :           8

Program di atas mendeklarasikan satu variabel untuk setiap tipe intrinsik, ditambah dua variabel real berkind berbeda yang dibahas pada bagian berikutnya. Pada keluaran, nilai logical ditulis T untuk true dan F untuk false, sedangkan nilai real ditampilkan dengan sekitar tujuh digit di bagian desimal. Deklarasi character(len=12) menetapkan panjang teks 12 karakter, dan nilai yang lebih pendek dilengkapi spasi di belakangnya hingga penuh. Oleh karena itu, intrinsik trim dipanggil untuk membuang spasi kosong di akhir teks, sedangkan operator // menggabungkan dua nilai character sebelum dicetak. Penugasan teks yang lebih panjang daripada deklarasi justru memotong sisa karakternya, sehingga panjang len perlu dipilih dengan menyisakan ruang cadangan yang cukup.

Konsep kind dan konstanta presisi#

Setiap tipe intrinsik memiliki satu atau lebih kind, yaitu parameter yang memilih salah satu dari beberapa representasi internal untuk tipe tersebut. Dua variabel real berkind berbeda dapat berbeda presisi dan rentang walaupun tipenya sama. Intrinsik kind() melaporkan nilai kind sebuah variabel, dan keluaran program di atas menunjukkan kind(jumlah) bernilai 4, sedangkan kind(x64) bernilai 8.

Angka 4 dan 8 tersebut adalah pilihan gfortran, bukan jaminan standar. Standar hanya menentukan bahwa kind yang lebih besar menampung rentang dan presisi yang lebih baik, sedangkan angkanya sendiri bergantung compiler. Deklarasi dengan angka kind langsung seperti real(kind=8) karenanya rapuh ketika berpindah compiler.

Modul intrinsik iso_fortran_env menyediakan konstanta bernama yang menyatakan presisi dalam jumlah bit, sehingga pilihan menjadi portabel. Perintah use di baris kedua program di atas memuat real32 untuk presisi tunggal (single precision) dan real64 untuk presisi ganda (double precision). Modul yang sama juga memuat int32 dan int64 untuk memilih presisi integer, dengan penulisan yang sama persis. Tipe character dan logical juga menerima kind, tetapi kind default sudah cukup dalam praktik, dan pilihan presisi yang penting berada pada real serta integer.

fortran
! tidak-dijalankan
use, intrinsic :: iso_fortran_env, only : real32, real64
real(real64) :: pi
pi = 3.141592653589793_real64

Deklarasi real(real64) memilih representasi 64 bit, sedangkan sufiks _real64 pada literal memberi tahu compiler bahwa konstanta tersebut harus disimpan dengan kind yang sama. Apabila sufiks dihilangkan, literal dibulatkan ke real default terlebih dahulu, sehingga digit ekstra tidak pernah tersimpan. Sufiks kind seperti _real32 dan _real64 karenanya dituliskan pada setiap literal yang menuntut presisi tertentu. Sebelum konstanta dari iso_fortran_env menjadi cara yang lazim, presisi dipilih dengan intrinsik selected_real_kind(p), dan bentuk tersebut masih sering ditemui pada kode lama.

Kehilangan presisi pada real default#

Real default pada gfortran setara real32, yaitu presisi tunggal dengan sekitar enam sampai tujuh digit signifikan. Presisi semacam itu cukup untuk banyak keperluan sehari-hari, tetapi menjadi masalah ketika perhitungan menumpuk banyak operasi. Penyebab dasarnya bukan bug, melainkan fakta bahwa sebagian bilangan desimal tidak pernah dapat disimpan persis dalam sistem biner. Bilangan 0.10.1 yang pendek pada sistem desimal menjadi pecahan biner tak berhingga, sehingga hanya nilai terdekatnya yang tersimpan.

fortran presisi.f90
program presisi
  use, intrinsic :: iso_fortran_env, only : real32, real64
  implicit none

  real(real32) :: a32, b32
  real(real64) :: a64, b64

  a32 = 0.1_real32
  b32 = 0.2_real32
  a64 = 0.1_real64
  b64 = 0.2_real64

  print *, "digit presisi real32 :", precision(a32)
  print *, "digit presisi real64 :", precision(a64)
  print *, "0.1 + 0.2 real32 :", a32 + b32
  print *, "0.1 + 0.2 real64 :", a64 + b64
end program presisi
bash
# kompilasi program presisi menjadi berkas executable bernama presisi
gfortran presisi.f90 -o presisi
# jalankan program hasil kompilasi
./presisi
text Keluaran
 digit presisi real32 :           6
 digit presisi real64 :          15
 0.1 + 0.2 real32 :  0.300000012
 0.1 + 0.2 real64 :  0.30000000000000004

Kedua baris terakhir menghitung penjumlahan yang sama pada dua kind. Pada real32, hasilnya 0.300000012, yang berarti selisih dari 0.3 yang seharusnya sudah terlihat pada digit kedelapan. Pada real64, selisih baru muncul pada digit ketujuh belas, yaitu 0.30000000000000004.

Intrinsik precision() melaporkan jumlah digit desimal yang dapat diandalkan, yaitu 6 untuk real32 dan 15 untuk real64 pada gfortran, sedangkan intrinsik huge() dan range() dari kelompok yang sama melaporkan nilai terbesar serta rentang eksponen sebuah kind. Keluaran semacam ini menjadi alasan mengapa perbandingan dua nilai real dengan operator == berisiko, karena dua nilai yang secara matematis sama dapat tersimpan berbeda pada digit terakhir. Perbedaan semacam ini terasa ketika penjumlahan berulang, karena selisih kecil pada setiap operasi ikut menumpuk seiring banyaknya langkah.

Ringkasan#

  • Empat tipe intrinsik adalah integer, real, logical, dan character, dengan deklarasi berbentuk tipe :: nama.
  • Kind memilih representasi internal sebuah tipe, dan angkanya bergantung compiler, sehingga konstanta real32 dan real64 dari iso_fortran_env lebih aman daripada angka kind langsung.
  • Literal diberi sufiks kind seperti 0.1_real64 agar konstanta tersimpan pada kind yang diinginkan sejak awal.
  • Real default bertipe presisi tunggal dengan sekitar enam digit signifikan, sehingga 0.1 + 0.2 tidak persis 0.3.

Referensi#