Bilangan

Hierarki tipe integer dan float, bilangan rasional Rational, serta bilangan kompleks Complex beserta aritmetikanya.

Daftar isi

Topik ini membahas sistem bilangan pada Julia, yaitu tipe integer dan Float64 beserta hierarkinya, kemudian bilangan rasional Rational dan bilangan kompleks Complex. Pembahasan mencakup rentang tipe, perilaku overflow, pembagian biasa dan pembagian bulat, serta aritmetika pada pecahan dan bilangan kompleks. Setelah membacanya, tipe bilangan yang sesuai untuk sebuah perhitungan dapat dipilih dengan memahami konsekuensinya.

Integer dan tipe Int64#

Literal bilangan bulat tanpa titik desimal pada Julia menjadi Int64, yaitu integer berukuran 64 bit yang menampung nilai negatif dan positif. Batas bawah dan atasnya dapat dibaca dengan fungsi typemin() dan typemax().

julia
umur = 17
println(typeof(umur))
println(typemin(Int64))
println(typemax(Int64))
text Keluaran
Int64
-9223372036854775808
9223372036854775807

Rentang tersebut berlaku untuk seluruh perhitungan yang bertipe Int64. Apabila hasil operasi melampaui batas atasnya, Julia tidak menaikkan tipe secara otomatis dan tidak menampilkan error. Hasilnya justru bergulir ke sisi berlawanan, sebuah perilaku yang disebut overflow.

julia
println(typemax(Int64) + 1)
text Keluaran
-9223372036854775808

Menambahkan satu pada nilai terbesar menghasilkan nilai terkecil, dan tidak ada peringatan yang muncul. Perhitungan yang berpotensi melewati rentang perlu dirancang dengan sadar, misalnya dengan menggunakan BigInt untuk integer berukuran tak terbatas.

Float64 dan presisi biner#

Literal desimal seperti 0.1 menjadi Float64, yaitu bilangan pecahan IEEE 754 berukuran 64 bit. Karena Float64 menyimpan nilai dalam basis dua, banyak desimal yang tampak sederhana tidak dapat diwakili secara persis, dan hasilnya mengalami pembulatan (rounding) pada bit terakhir.

julia
println(0.1 + 0.2)
println(0.1 + 0.2 == 0.3)
println(isapprox(0.1 + 0.2, 0.3))
text Keluaran
0.30000000000000004
false
true

Jumlah 0.1 + 0.2 tercetak sebagai 0.30000000000000004, sedikit melenceng dari 0.3. Perbandingan == yang ketat bernilai false, sedangkan fungsi isapprox() yang membandingkan dengan toleransi bernilai true. Perilaku ini bukan kekhasan Julia, melainkan konsekuensi aritmetika pecahan basis dua yang diikuti hampir semua bahasa modern. Perbandingan hasil float sebaiknya menggunakan isapprox() atau batas toleransi yang dinyatakan eksplisit.

Pembagian biasa, pembagian bulat, dan sisa#

Operator / selalu menghasilkan Float64, bahkan ketika kedua operandnya integer dan hasilnya bulat sempurna. Untuk pembagian bulat, Julia menyediakan operator ÷ beserta fungsi div(), sedangkan sisa pembagian diperoleh dari rem().

julia
println(4 / 2)
println(typeof(4 / 2))
println(7 ÷ 2)
println(div(7, 2))
println(rem(7, 2))
println(1 / 0)
try
    println(div(1, 0))
catch err
    println(typeof(err))
end
text Keluaran
2.0
Float64
3
3
1
Inf
DivideError

Pembagian 4 / 2 menghasilkan 2.0 bertipe Float64, sedangkan 7 ÷ 2 menghasilkan 3 bertipe integer karena bagian pecahannya dipotong. Pembagian 1 / 0 tidak memicu error dan menghasilkan Inf, yaitu nilai khusus untuk tak hingga. Sebaliknya, div(1, 0) memicu DivideError karena pembagian bulat dengan nol tidak memiliki hasil yang terdefinisi. Pemilihannya menyesuaikan kebutuhan, yaitu pembagian biasa menggunakan / dan pembagian bulat menggunakan ÷ atau div().

Hierarki tipe bilangan#

Seluruh tipe bilangan pada Julia berada di bawah tipe abstrak Number. Cabang Real menampung bilangan riil dan membaginya menjadi Integer, Rational, dan AbstractFloat, sedangkan cabang lain menampung bilangan kompleks. Di bawah Integer terdapat Signed untuk integer bertanda dan Unsigned untuk integer tanpa tanda. Diagram berikut memperlihatkan susunannya, dengan kotak biru untuk tipe abstrak dan kotak kuning untuk tipe konkret yang digunakan sehari-hari.

NumberRealComplex{Int64}IntegerRational{Int64}AbstractFloatSignedUnsignedInt64UInt64Float64Biru = tipe abstrak, kuning = tipe konkret
Hierarki tipe bilangan dari Number hingga tipe konkret

Posisi sebuah tipe dalam hierarki dapat dilacak dengan fungsi supertype(). Rantai berikut menaiki hierarki dari Int64 hingga Any, yaitu akar seluruh tipe Julia.

julia
println(supertype(Int64))
println(supertype(Signed))
println(supertype(Integer))
println(supertype(Real))
println(supertype(Number))
text Keluaran
Signed
Integer
Real
Number
Any

Rantai untuk Float64 menyusuri cabang yang berbeda, yaitu AbstractFloat, Real, Number, lalu Any. Keduanya bertemu kembali di Real, dan perbedaannya hanyalah cabang tengahnya. Hierarki inilah yang diacu ketika sebuah fungsi menerima argumen bertipe abstrak seperti Real, sehingga baik Int64 maupun Float64 diterima.

Bilangan rasional Rational#

Operator // membentuk bilangan rasional bertipe Rational{Int64}, yaitu pasangan pembilang dan penyebut integer. Julia menyederhanakan pecahan secara otomatis dengan faktor persekutuan terbesar, sehingga 6//8 langsung menjadi pecahan tiga per empat, 34 \frac{3}{4} .

julia
r = 3//4
println(r)
println(typeof(r))
println(1//3 + 1//6)
println(numerator(r), "/", denominator(r))
println(float(r))
text Keluaran
3//4
Rational{Int64}
1//2
3/4
0.75

Penjumlahan 1//3 + 1//6 menghasilkan 1//2 secara eksak tanpa pembulatan, sebagaimana dapat diperiksa pada persamaan berikut.

13+16=26+16=12 \frac{1}{3} + \frac{1}{6} = \frac{2}{6} + \frac{1}{6} = \frac{1}{2}

Fungsi numerator() dan denominator() mengambil pembilang serta penyebutnya, sedangkan float() mengonversinya ke Float64 beserta presisi binernya. Karena perhitungannya eksak, Rational cocok untuk perhitungan yang tidak boleh kehilangan ketelitian akibat pembulatan.

Sebagai contoh yang menyatukan materi rasional, berkas berikut menghitung rata-rata tiga nilai ujian dan menyimpannya sebagai bilangan rasional.

julia rata-rata-nilai.jl
# Statistik sederhana: rata-rata tiga nilai ujian
a = 82
b = 90
c = 76

jumlah = a + b + c
rata_rata = jumlah // 3

println("Jumlah: ", jumlah)
println("Rata-rata (rasional): ", rata_rata)
println("Rata-rata (float): ", float(rata_rata))
text Keluaran
Jumlah: 248
Rata-rata (rasional): 248//3
Rata-rata (float): 82.66666666666667

Rata-rata 2483 \frac{248}{3} tidak dapat dinyatakan sebagai desimal berhingga, dan Rational menyimpannya persis sebagai 248//3. Konversi ke Float64 baru menghasilkan angka dengan pembulatan biner.

Bilangan kompleks Complex#

Akhiran im menyatakan satuan imajiner, sehingga 2 + 3im menjadi bilangan kompleks bertipe Complex{Int64}. Julia menyediakan aritmetika lengkap untuk tipe ini, termasuk penjumlahan, perkalian, konjugat dengan conj(), dan nilai mutlak dengan abs().

julia
z = 2 + 3im
println(z)
println(typeof(z))
println(z * conj(z))
println(abs(3 + 4im))
println(real(z), " dan ", imag(z))
text Keluaran
2 + 3im
Complex{Int64}
13 + 0im
5.0
2 dan 3

Perkalian z dengan konjugatnya menghasilkan 13 + 0im, yaitu jumlah kuadrat bagian riil dan imajinernya.

(2+3i)(2−3i)=4+9=13 (2 + 3i)(2 - 3i) = 4 + 9 = 13

Fungsi abs() menghitung panjang vektor bilangan kompleks pada bidang, sehingga abs(3 + 4im) bernilai 5.0. Fungsi real() dan imag() mengambil bagian riil serta imajinernya. Tipe hasil aritmetika kompleks mengikuti tipe bagiannya, dan operasi yang melibatkan Float64 menghasilkan Complex{Float64}.

Kesalahan umum#

Kesalahan pertama adalah membandingkan hasil float dengan ==. Karena presisi biner Float64, jumlah 0.1 + 0.2 tidak sama persis dengan 0.3 dan perbandingannya bernilai false. Perbaikannya menggunakan isapprox() yang membandingkan dengan toleransi, sebagaimana ditunjukkan pada bagian presisi biner.

Kesalahan kedua adalah mengira operator / menghasilkan integer. Hasilnya selalu Float64, sehingga kode yang menuntut tipe integer perlu beralih ke ÷ atau div(). Kebalikannya juga perlu diwaspadai, yaitu mengira div(1, 0) menghasilkan Inf, padahal pembagian bulat dengan nol memicu DivideError.

Kesalahan ketiga adalah membaca hasil integer di dekat typemax(Int64) tanpa menyadari overflow. Perkalian yang terus berlipat, misalnya pada perhitungan faktorial, melampaui rentang Int64 dengan cepat, dan hasilnya bergulir tanpa peringatan. Gunakan BigInt apabila nilai yang dihitung dapat tumbuh melewati rentang tersebut.

Ringkasan#

  • Literal integer menjadi Int64 dengan rentang typemin() sampai typemax(), dan hasil di luar rentang bergulir tanpa error.
  • Literal desimal menjadi Float64 yang mengikuti aritmetika biner, sehingga perbandingan float menggunakan isapprox().
  • Operator / selalu menghasilkan Float64, sedangkan ÷ atau div() dan rem() menangani pembagian bulat beserta sisanya.
  • Operator // membentuk Rational{Int64} yang dihitung eksak, dan akhiran im membentuk Complex{Int64} beserta aritmetikanya.

Referensi#