Ini semua masalah penyimpanan yang memadai dan algoritme untuk memperlakukan angka sebagai bagian yang lebih kecil. Anggaplah Anda memiliki kompiler di mana an int
hanya bisa 0 hingga 99 dan Anda ingin menangani angka hingga 999999 (kami hanya akan mengkhawatirkan bilangan positif di sini agar tetap sederhana).
Anda melakukannya dengan memberikan setiap angka tiga int
dan menggunakan aturan yang sama yang (seharusnya) Anda pelajari di sekolah dasar untuk penjumlahan, pengurangan, dan operasi dasar lainnya.
Dalam pustaka presisi arbitrer, tidak ada batasan tetap pada jumlah tipe dasar yang digunakan untuk merepresentasikan angka kita, apa pun yang dapat ditampung oleh memori.
Penambahan misalnya 123456 + 78
:
12 34 56
78
-- -- --
12 35 34
Bekerja dari ujung yang paling tidak signifikan:
- bawaan awal = 0.
- 56 + 78 + 0 carry = 134 = 34 dengan 1 carry
- 34 + 00 + 1 carry = 35 = 35 dengan 0 carry
- 12 + 00 + 0 carry = 12 = 12 dengan 0 carry
Faktanya, ini adalah bagaimana penambahan biasanya bekerja pada level bit di dalam CPU Anda.
Pengurangan serupa (menggunakan pengurangan jenis dasar dan meminjam alih-alih membawa), perkalian dapat dilakukan dengan penjumlahan berulang (sangat lambat) atau perkalian silang (lebih cepat) dan pembagian lebih rumit tetapi dapat dilakukan dengan menggeser dan mengurangi angka-angka terlibat (divisi panjang yang akan Anda pelajari sebagai seorang anak).
Saya sebenarnya telah menulis perpustakaan untuk melakukan hal-hal semacam ini menggunakan kekuatan maksimum sepuluh yang dapat dimasukkan ke dalam bilangan bulat ketika dikuadratkan (untuk mencegah luapan saat mengalikan dua int
bersama, seperti 16-bit int
yang dibatasi ke 0 hingga 99 hingga menghasilkan 9.801 (<32.768) saat dikuadratkan, atau 32-bit int
menggunakan 0 hingga 9.999 untuk menghasilkan 99.980.001 (<2.147.483.648)) yang sangat memudahkan algoritme.
Beberapa trik yang harus diperhatikan.
1 / Saat menjumlahkan atau mengalikan angka, alokasikan terlebih dahulu ruang maksimum yang dibutuhkan kemudian kurangi nanti jika Anda merasa jumlahnya terlalu banyak. Misalnya, menambahkan dua angka 100- "digit" (dimana digit adalah int
) tidak akan pernah memberi Anda lebih dari 101 digit. Mengalikan angka 12 digit dengan angka 3 digit tidak akan menghasilkan lebih dari 15 digit (tambahkan jumlah digit).
2 / Untuk kecepatan tambahan, normalkan (kurangi penyimpanan yang diperlukan untuk) nomor hanya jika benar-benar diperlukan - perpustakaan saya memiliki ini sebagai panggilan terpisah sehingga pengguna dapat memutuskan antara kecepatan dan masalah penyimpanan.
3 / Penjumlahan bilangan positif dan negatif adalah pengurangan, dan mengurangi bilangan negatif sama dengan menjumlahkan positif yang setara. Anda dapat menyimpan cukup banyak kode dengan meminta metode tambah dan kurang memanggil satu sama lain setelah menyesuaikan tanda.
4 / Hindari mengurangi angka besar dari angka kecil karena Anda selalu mendapatkan angka seperti:
10
11-
-- -- -- --
99 99 99 99 (and you still have a borrow).
Sebagai gantinya, kurangi 10 dari 11, lalu negasikan:
11
10-
--
1 (then negate to get -1).
Berikut adalah komentar (diubah menjadi teks) dari salah satu perpustakaan tempat saya harus melakukan ini. Sayangnya, kode itu sendiri memiliki hak cipta, tetapi Anda mungkin dapat memilih informasi yang cukup untuk menangani empat operasi dasar. Asumsikan berikut ini -a
dan -b
mewakili angka negatif dan a
dan b
adalah angka nol atau positif.
Untuk penjumlahan , jika tanda berbeda, gunakan pengurangan negasi:
-a + b becomes b - a
a + -b becomes a - b
Untuk pengurangan , jika tanda berbeda, gunakan penjumlahan dari negasi:
a - -b becomes a + b
-a - b becomes -(a + b)
Juga penanganan khusus untuk memastikan kita mengurangi angka kecil dari besar:
small - big becomes -(big - small)
Perkalian menggunakan matematika tingkat awal sebagai berikut:
475(a) x 32(b) = 475 x (30 + 2)
= 475 x 30 + 475 x 2
= 4750 x 3 + 475 x 2
= 4750 + 4750 + 4750 + 475 + 475
Cara di mana ini dicapai melibatkan mengekstraksi masing-masing digit dari 32 satu per satu (mundur) kemudian menggunakan add untuk menghitung nilai yang akan ditambahkan ke hasil (awalnya nol).
ShiftLeft
dan ShiftRight
operasi digunakan untuk mengalikan atau membagi dengan cepat LongInt
dengan nilai bungkus (10 untuk matematika "nyata"). Pada contoh di atas, kita tambahkan 475 ke nol 2 kali (digit terakhir 32) untuk mendapatkan 950 (hasil = 0 + 950 = 950).
Kemudian kita geser ke kiri 475 untuk mendapatkan 4750 dan geser kanan 32 untuk mendapatkan 3. Tambahkan 4750 ke nol sebanyak 3 kali untuk mendapatkan 14250 kemudian tambahkan hasil 950 untuk mendapatkan 15200.
Pergeseran kiri 4750 untuk mendapatkan 47500, geser kanan 3 untuk mendapatkan 0. Karena pergeseran kanan 32 sekarang menjadi nol, kita selesai dan, sebenarnya 475 x 32 sama dengan 15200.
Pembagian juga rumit tetapi berdasarkan aritmatika awal (metode "gazinta" untuk "masuk ke"). Pertimbangkan pembagian panjang berikut untuk 12345 / 27
:
457
+-------
27 | 12345 27 is larger than 1 or 12 so we first use 123.
108 27 goes into 123 4 times, 4 x 27 = 108, 123 - 108 = 15.
---
154 Bring down 4.
135 27 goes into 154 5 times, 5 x 27 = 135, 154 - 135 = 19.
---
195 Bring down 5.
189 27 goes into 195 7 times, 7 x 27 = 189, 195 - 189 = 6.
---
6 Nothing more to bring down, so stop.
Oleh karena 12345 / 27
itu 457
dengan sisa 6
. Memeriksa:
457 x 27 + 6
= 12339 + 6
= 12345
Ini diimplementasikan dengan menggunakan variabel draw-down (awalnya nol) untuk menurunkan segmen 12345 satu per satu hingga lebih besar atau sama dengan 27.
Kemudian kita cukup mengurangi 27 dari itu sampai kita mendapatkan di bawah 27 - jumlah pengurangan adalah ruas yang ditambahkan ke garis atas.
Ketika tidak ada lagi segmen untuk diturunkan, kami mendapatkan hasil kami.
Ingatlah bahwa ini adalah algoritme yang cukup mendasar. Ada cara yang jauh lebih baik untuk melakukan aritmatika kompleks jika bilangan Anda sangat besar. Anda dapat melihat sesuatu seperti GNU Multiple Precision Arithmetic Library - secara substansial lebih baik dan lebih cepat daripada perpustakaan saya sendiri.
Itu memang memiliki kesalahan yang agak disayangkan karena itu akan keluar begitu saja jika kehabisan memori (kesalahan yang agak fatal untuk perpustakaan tujuan umum menurut saya) tetapi, jika Anda bisa melihat melewati itu, itu cukup bagus dalam apa yang dilakukannya.
Jika Anda tidak dapat menggunakannya untuk alasan lisensi (atau karena Anda tidak ingin aplikasi Anda keluar begitu saja tanpa alasan yang jelas), Anda setidaknya bisa mendapatkan algoritme dari sana untuk diintegrasikan ke dalam kode Anda sendiri.
Saya juga menemukan bahwa badan-badan di MPIR (garpu GMP) lebih setuju untuk diskusi tentang perubahan potensial - mereka tampak seperti kelompok yang lebih ramah pengembang.