Anda di halaman 1dari 50

Pendahuluan

1.0 Introduction
Buku ini mengajarkan metode komputasi numerik yang praktis, efisien, dan elegan. Kita
menganggap seluruh buku ini bahwa anda dan pembaca, memiliki tugas-tugas tertentu yang
ingin anda lakukan. Kita melihat tugas kita ,sebagai pendidik tentang cara untuk berproses.
Kadang-kadang kita mungkin mencoba untuk mengalihkan secara singkat ke sisi jalan yang
sangat indah yang akan memandu Anda sepanjang jalan yang mengarah ke tujuan praktis.
Sepanjang buku ini, Anda akan menemukan tanpa rasa takut menyunting, mengatakan apa yang
harus dan tidak harus dilakukan. Nada preskriptif Ini hasil dari keputusan sadar pada bagian kita,
dan kita berharap bahwa Anda tidak akan menemukan yang menjengkelkan. Kita tidak
mengklaim bahwa saran kita adalah sempurna! Sebaliknya, kita bereaksi terhadap
kecenderungan, dalam literatur buku teks dari perhitungan, untuk membahas setiap metode yang
mungkin yang pernah diciptakan, tanpa pernah menawarkan penilaian praktis tentang jasa relatif.
Kita lakukan, oleh karena itu menawarkan penilaian praktis kita kapan saja kita bisa. Ketika
Anda mendapatkan pengalaman, Anda akan membentuk pendapat Anda sendiri tentang
bagaimana diandalkan saran kita adalah. Yakinlah bahwa itu tidak sempurna!
Kita menganggap bahwa Anda mampu membaca program komputer di C ++. pertanyaan,
"Mengapa C ++?", C ++ Adalah salah satu yang rumit. Untuk saat ini, cukup untuk mengatakan
bahwa kita ingin bahasa dengan sintaks C (karena yang paling universal dibaca oleh khalayak
kita), yang memiliki penekanan dari edisi ketiga ini), dan yang sangat ke belakang kompatibel
dengan beberapa trik lama, tapi mapan dan teruji, dalam pemrograman numerik. Yang cukup
banyak membawa kita ke C ++, meskipun Java (dan C # terkait erat) pesaing dekat.
Kejujuran mendorong kita untuk menunjukkan bahwa dalam sejarah 20-tahun dari Numerical
Recipes, kita tidak pernah benar dalam prediksi mengenai masa depan bahasa pemrograman
untuk pemrograman ilmiah, tidak sekali! Pada berbagai kesempatan kita meyakinkan diri kita
sendiri bahwa gelombang masa depan ilmiah akan. . . Fortran. . . Pascal. . . C. . . Fortran 90 (atau
95 atau 2000). . . Mathematica. . . Matlab. . . C ++ atau Java. . . . beberapa ini terus menikmati
sukses dan memiliki pengikut yang signifikan (tidak termasuk Pascal!). Tidak ada, namun saat
perintah mayoritas, atau bahkan sejumlah besar, pengguna ilmiah
Dengan edisi ini, kita tidak lagi mencoba untuk memprediksi masa depan bahasa pemrograman.
Sebaliknya, kita ingin cara ini berguna untuk komunikasi ide-ide tentang program scientific. Kita
berharap bahwa ide-ide ini melampaui bahasa, C ++, di mana kita mengekspresikan mereka.
Ketika kita termasuk program dalam teks, mereka terlihat seperti ini:
calendar.h void flmoon(const Int n, const Doub &frac) {

Int nph, Int &jd, Rutinitas kita mulai dengan ringkasan pengantar komentar tujuan mereka dan
menjelaskan urut memanggil mereka. Keseharian ini menghitung fase bulan. Mengingat bilangan
bulat n dan sebuah kode nph untuk fase yang diinginkan (nph D 0 untuk bulan baru, 1 untuk
kuartal pertama, 2 untuk penuh, 3 untuk kuartal terakhir), rutinitas mengembalikan Julian Day
Number jd, dan bagian pecahan dari hari frac akan ditambahkan ke dalamnya, dari fase seperti n
sejak Januari, 1900. Greenwich Mean Time adalah diasumsikan.
const Doub RAD=3.141592653589793238/180.0;
Int i;
Doub am,as,c,t,t2,xtra;
c=n+nph/4.0; This is how we comment an individual line.
t=c/1236.85;
t2=t*t;
as=359.2242+29.105356*c;

You

arent

really

intended

to

understand
am=306.0253+385.816918*c+0.010730*t2; this algorithm, but it does
work!
jd=2415020+28*n+7*nph;
xtra=0.75933+1.53058868*c+((1.178e-4)-(1.55e-7)*t)*t2;
if (nph == 0 || nph == 2)
xtra += (0.1734-3.93e-4*t)*sin(RAD*as)-0.4068*sin(RAD*am);
else if (nph == 1 || nph == 3)
xtra += (0.1721-4.0e-4*t)*sin(RAD*as)-0.6280*sin(RAD*am);
else throw("nph is unknown in flmoon");

This

condition.
i=Int(xtra >= 0.0 ? floor(xtra) : ceil(xtra-1.0));
jd += i;
frac=xtra-i;
}

indicates

an

error

Catatan konvensi kita menangani semua kesalahan dan kasus luar biasa dengan negara-ment
seperti melemparkan ("beberapa pesan kesalahan") ;. Sejak C ++ tidak memiliki built-in kelas
pengecualian untuk tipe char *, melaksanakan hasil pernyataan ini dalam program pembatalan
cukup kasar. Namun kita akan menjelaskan secara _1.5.1 bagaimana untuk mendapatkan hasil
yang lebih baik tanpa harus memodifikasi kode sumber.
1.0.1 Bukan Numerical Recipes
Kita ingin menggunakan platform dari bagian pengantar ini untuk menekankan apa
yangNumerical Recipes tidak:
1)
Numerical Recipes bukan buku teks pada pemrograman, atau praktik Programming
terbaik, atau C ++, atau rekayasa perangkat lunak. Kita tidak menentang pemrograman yang
baik. Kita mencoba untuk berkomunikasi praktek pemrograman yang baik kapan saja kita bisa,
tetapi hanya kebetulan untuk tujuan utama kita, yaitu untuk mengajarkan bagaimana praktis
metode numerik benar-benar bekerja. Kesatuan gaya dan subordinasi fungsi untuk standardisasi
yang diperlukan dalam pemrograman yang baik (atau rekayasa perangkat lunak) buku teks hanya
tidak apa yang kita miliki dalam pikiran untuk buku ini. Setiap bagian dalam buku ini memiliki
fokus metode komputasi tertentu. Tujuan kita adalah untuk menjelaskan dan menggambarkan
metode yang sejelas mungkin. Tidak ada gaya pemrograman tunggal yang terbaik untuk semua
metode tersebut, dan, sesuai, gaya kita bervariasi dari bagian ke bagian.
2)
Numerical Recipes bukan program perpustakaan. Yang mungkin akan mengejutkan Anda
jika Anda adalah salah satu dari banyak ilmuwan dan insinyur yang menggunakan kode sumber
kita secara teratur. perpustakaan Apa membuat kode kita tidak program adalah bahwa hal itu
menuntut intelektual komitmen lebih besar dari pengguna dari program perpustakaan harus
dilakukan. Jika Anda belum membaca bagian rutinitas yang menyertai dan pergi melalui jalur
rutin demi baris untuk mengerti cara kerjanya, maka Anda menggunakannya pada bahaya besar!
Kita menganggap ini sebuah fitur, bukan bug, karena tujuan utama kita adalah untuk mengajar
metode, tidak memberikan solusi yang diberikan. Buku ini tidak termasuk latihan formal,
sebagian karena kita mempertimbangkan kode setiap bagian untuk menjadi latihan: Jika Anda
bisa memahami setiap baris kode, maka Anda mungkin telah menguasai bagiannnya. Ada
beberapa program yang baik perpustakaan komersial [1,2] dan lingkungan numerik terintegrasi
[3-5] tersedia. Sumber sebanding gratis yang tersedia, baik perpustakaan Program [6,7] dan
lingkungan terpadu [10/08]. Bila Anda ingin solusi ditemukan, kita menyarankan Anda
menggunakan salah satu dari ini. Numerical Recipes dimaksudkan sebagai buku resep untuk
memasak, bukan menu restoran untuk pengunjung.
1.0.2 Pertanyaan yang Sering Diajukan
1)

Bagaimana cara menggunakan NR rutinitas dengan program saya sendiri?

Cara termudah adalah dengan menempatkan a bunch of # termasuk di bagian atas dari program
Anda. Selalu mulai dengan nr3.h, karena yang mendefinisikan beberapa diperlukan kelas utilitas
dan fungsi (lihat _1.4 untuk lebih banyak tentang hal ini). Sebagai contoh, berikut adalah cara
Anda menghitung mean dan varians dari Bilangan Julian Hari pertama 20 bulan penuh setelah
tanggal 1900. (Sekarang ada sepasang berguna dalam jumlah!)
#include "nr3.h"
#include "calendar.h"
#include "moment.h"
Int main(void) { const Int NTOT=20;

Int i,jd,nph=2;

Doub frac,ave,vrnce; VecDoub data(NTOT); for (i=0;i<NTOT;i++) {


flmoon(i,nph,jd,frac);
data[i]=jd;
}
avevar(data,ave,vrnce);
cout << "Average = " << setw(12) << ave;

cout << " Variance = " << setw(13) << vrnce << endl; return 0;
}
Pastikan bahwa file kode sumber NR berada di tempat yang compiler Anda dapat menemukan
mereka untuk # include. Mengkompilasi dan menjalankan file di atas. (Kita tidak bisa
memberitahu Anda bagaimana untuk melakukan bagian ini.) Output harus sesuatu seperti ini:
Rata-rata = 2.41532e + 06 Variance = 30480,7
2)

Ya, tapi di mana saya benar-benar mendapatkan kode sumber NR sebagai file komputer?

Anda dapat membeli sebuah kode, atau kode satu kali download, di situs Web
http://www.nr.com, atau Anda bisa mendapatkan kode pada media yang diterbitkan oleh
Cambridge University Press (misalnya, dari Amazon.com atau toko buku online atau fisik favorit
Anda). Kode ini dilengkapi dengan pribadi, lisensi pengguna tunggal (lihat Lisensi dan Informasi
Hukum pada hal. Xix). Alasan bahwa buku (atau versi elektronik) dan lisensi kode yang dijual
secara terpisah adalah untuk membantu menjaga menurunkan harga masing-masing. Juga,

membuat produk ini memisahkan memenuhi kebutuhan lebih banyak pengguna: Perusahaan
Anda atau lembaga pendidikan dapat memiliki lisensi situs meminta mereka.
3)
Bagaimana saya tahu file mana yang # include? Sulit untuk memilah dependensi antara
semua rutinitas.
Dalam margin selanjutnya setiap daftar kode adalah nama file kode sumber . Buatlah daftar file
kode sumber yang Anda gunakan. Lalu pergi ke http://www.nr.com/dependencies dan klik pada
nama setiap file kode sumber. Halaman Web di-teractive akan kembali daftar # termasuk yang
diperlukan, dalam urutan yang benar, untuk memenuhi semua dependensi. Gambar 1.0.1 akan
memberikan gambaran tentang bagaimana ini bekerja.
4)

Apa semua ini Doub, Int, VecDoub, dll, stuff?

Kita selalu menggunakan jenis definisi, tidak jenis built-in, sehingga mereka dapat didefinisikan
ulang jika diperlukan. Definisi berada di nr3.h. Umumnya, seperti yang Anda bisa menebak,
Doub berarti ganda, Int berarti int, dan sebagainya. Konvensi kita adalah untuk memulai semua
jenis definisi dengan huruf besar. VecDoub adalah jenis kelas vektor. Rincian tentang jenis kita
berada di _1.4.
5)

Apa Numerical Recipes Webnotes?

Numerical Recipes Webnotes adalah dokumen, diakses di Web, yang mencakup beberapa daftar
pelaksanaan kode, atau topik yang sangat khusus lainnya, yang tidak termasuk dalam versi kertas
buku. Daftar semua Webnotes
1.0 Introduction

Tested Operating Systems and Compilers


O/S

Compiler

Microsoft Windows XP SP2 Visual C++ ver. 14.00 (Visual Studio 2005)
Microsoft Windows XP SP2 Visual C++ ver. 13.10 (Visual Studio 2003)
Microsoft Windows XP SP2 Intel C++ Compiler ver. 9.1
Novell SUSE Linux 10.1

GNU GCC (g++) ver. 4.1.0

Red Hat Enterprise Linux 4 (64-bit) GNU GCC (g++) ver. 3.4.6 and ver. 4.1.0
Red Hat Linux 7.3

Intel C++ Compiler ver. 9.1

Apple Mac OS X 10.4 (Tiger) Intel Core

GNU GCC (g++) ver. 4.0.1

http://www.nr.com/webnotes. Dengan memindahkan beberapa bahan khusus ke dalam Webnotes,


kita mampu untuk menekan ukuran dan harga buku kertas. Webnotes secara otomatis termasuk
dalam versi elektronik dari buku; lihat pertanyaan berikutnya.
6)
Saya posting-kertas orang. Saya ingin Numerical Recipes di laptop saya. Di mana saya
mendapatkan lengkap, versi elektronik sepenuhnya?
Sebuah versi elektronik sepenuhnya dari Numerical Recipes tersedia dengan berlangganan
tahunan. Anda dapat berlangganan bukan, atau di samping, memiliki salinan kertas buku.
Langganan A dapat diakses melalui Web, download, dicetak, dan, tidak seperti versi kertas,
selalu up to date dengan koreksi terbaru. Sejak versi elektronik tidak berbagi batas halaman dari
versi cetak, ia akan tumbuh dari waktu ke waktu dengan penambahan bagian yang sama sekali
baru, yang tersedia hanya secara elektronik. Ini, kita berpikir, adalah masa depan Numerical
Recipes dan mungkin teknis buku merujuk secara umumnya. Kita mengantisipasi berbagai
format elektronik, berubah dengan waktu sebagai teknologi untuk layar dan hak pengelolaan
terus meningkatkan: Kita menempatkan penekanan besar pada kenyamanan pengguna dan
kegunaan. Lihat http://www.nr.com/electronic untuk informasi lebih lanjut.
7)

Apakah ada kesalahan di NR?

Tentu saja! Sekarang, sebagian besar kode NR memiliki manfaat penggunaan lama oleh
komunitas pengguna yang besar, tetapi akan terdapat kesalahan. Lihatlah http://www.nr.com
informasi tentang kesalahan, atau melaporkan kejelasan yang baru.
1.0.3 Komputasi Lingkungan dan Program Validasi
Kode dalam buku ini harus berjalan tanpa modifikasi pada setiap compiler yang
mengimplementasikan standar ANSI / ISO C ++, seperti yang dijelaskan, misalnya, dalam
Stroustrup buku [11].
Sebagai pengganti untuk sejumlah besar perangkat keras dan konfigurasi perangkat lunak, kita
telah menguji semua kode dalam buku ini pada kombinasi dari sistem operasi dan kompiler
ditampilkan dalam tabel di atas.
Dalam memvalidasi kode, kita telah mengambil langsung dari bentuk yang dapat dibaca mesin
naskah buku, sehingga kita telah menguji apa yang dicetak. (Ini tidak, tentu saja, berarti bahwa
kode tersebut adalah bug-free!)
1.0.4 Tentang Referensi
Anda akan menemukan referensi, dan saran untuk membaca lebih lanjut, terdaftar pada akhir
bagian dari buku ini. Referensi dikutip dalam teks dengan nomor tanda kurung seperti ini
[12].Kita tidak berpura-pura setiap tingkat kelengkapan bibliografi dalam buku ini. Untuk topik
mana literatur sekunder substansial ada (pembahasan dalam buku teks, ulasan, dll) kita sering

membatasi referensi kita untuk beberapa sumber sekunder lebih berguna, terutama mereka
dengan referensi yang baik untuk literatur primer. Di mana literatur sekunder yang ada tidak
mencukupi, kita memberikan referensi ke sumber primer beberapa yang dimaksudkan untuk
melayani sebagai titik awal untuk membaca lebih lanjut, bibliografi tidak lengkap untuk
lapangan.Karena kemajuan sedang berlangsung, tidak dapat dihindari bahwa referensi kita untuk
banyak topik yang sudah, atau akan segera menjadi, dari tanggal. Kita telah mencoba untuk
memasukkan referensi yang lebih tua yang baik untuk "maju" Web pencarian: Sebuah pencarian
untuk makalah yang lebih baru yang mengutip referensi yang diberikan harus membawa Anda ke
pekerjaan terbaru.Referensi web dan URL menyajikan masalah, karena tidak ada cara bagi kita
untuk menjamin bahwa mereka akan tetap ada ketika Anda mencari mereka. Sebuah tanggal
seperti 2007 + berarti "itu ada pada tahun 2007." Kita berusaha memberikan kutipan yang cukup
lengkap bagi Anda untuk menemukan dokumen dengan pencarian Web, bahkan jika itu telah
pindah dari lokasi yang terdaftar. Urutan referensi tercantum belum tentu signifikan. Itu kembali
flects kompromi antara daftar kutipan referensi dalam urutan yang dikutip, dan daftar saran
untuk bacaan lebih lanjut dalam rangka kasar diprioritaskan, dengan yang paling berguna
pertama.
1.0.5 Tentang "Topik Advanced"
Himpunan materi dalam jenis yang lebih kecil, seperti ini, merupakan sinyal "topik lanjutan,"
salah satu di luar argumen bab utama, atau salah satu yang dibutuhkan dari Anda lebih dari yang
biasa latar belakang matematika diasumsikan, atau (dalam beberapa kasus) diskusi yang lebih
spekulatif atau algoritma yang diuji kurang baik. Tidak ada yang penting akan hilang jika Anda
melewatkan topik-topik lanjutan pada bacaan pertama dari buku.
Berikut adalah fungsi untuk mendapatkan Julian Day Number dari tanggal kalender.
calendar.h

Int julday(const Int mm, const Int id, const Int iyyy) {

In this routine julday returns the Julian Day Number that begins at noon of the calendar date
specified by month mm, day id, and year iyyy, all integer variables. Positive year signifies A.D.;
negative, B.C. Remember that the year after 1 B.C. was 1 A.D.
const Int IGREG=15+31*(10+12*1582);

Gregorian Calendar adopted Oct. 15, 1582.

Int ja,jul,jy=iyyy,jm;
if (jy

== 0) throw("julday: there is no year zero.");

if (jy

< 0)

if (mm > 2)
jm=mm+1;

++jy;
{

} else {
--jy;
jm=mm+13;
}
jul = Int(floor(365.25*jy)+floor(30.6001*jm)+id+1720995);
if (id+31*(mm+12*iyyy) >= IGREG) {
Gregorian Cal- endar.

Test whether to change to

ja=Int(0.01*jy);

jul += 2-ja+Int(0.25*ja);
}
return jul;
}

1.1 Kesalahan, Akurasi dan stabilitas


Banyak komputer yang memiliki penyimpanan yang tak terbatas tetapi ada beberapa
diantaranya dapat dihitung dalam jumlah bits (binary digits) atau bytes (groups of 8 bits).
Hampir semua komputer memungkinkan programmer memilih antara penggambaran atau tipe
data.. Tipe data dapat berbeda dalam jumlah angka yang digunakan (panjang kata), selain itu
juga dalam hal yang lebih mendasar yaitu nomor yang tersimpan (seperti int) atau penyebaran
titik (seperti penyebaran atau penggandaan).
Representasi sebuah bilangan bulat yang tepat. Aritmatika antara jumlah dalam
representasi bilangan bulat juga tepat, dengan syarat bahwa (i) jawabannya adalah tidak di luar
jangkauan (biasanya, ditandatangani) bilangan bulat yang dapat diwakili, dan (ii) pembagian
ditafsirkan memproduksi hasil bilangan bulat, membuang setiap sisa bilangan bulat.
1.1.1 Representasi Penyebaran Titik
Dalam representasi penyebaran titik, bilangan yang direpresentsikan secara internal
diwakili oleh tanda s (ditafsirkan dengan tambah atau kurang), bilangan bulat eksak eksponen
diwakili oleh E, dan biner diwakili oleh M. tanda-tanda tersebut memiliki hubngan

Di mana b adalah dasar dari representasi (b hampir selalu = 2 ), dan e adalah simpangan
eksponen, bilangan bulat tetap konstan untuk setiap mesin yang diberikan dan representasi.

float

Value

Any

1245

Any

Any

Nonzero

+ 0.0

0.0

255

255

Any

255

nonzero

Any

1246

Any

Any

Nonzero

+ 0.0

0.0

257

257

Any

257

nonzero

NaN

NaN

*unnormalized values

Beberapa pola penyebaran titik dapat pada prinsipnya merupakan jumlah yang sama. Jika
b = 2, misalnya, mantisa dengan memimpin (urutan tertinggi) nol potongan kecil dapat dibiarkan
bergeser, oleh, dikalikan dengan kekuatan 2, jika eksponen menurun dengan jumlah yang
kompensasi. Pola potongan kecil yang "misalnya sebelah kiri bergeser sebagai dapat" dihidupkan
normal.
Hampir semua prosesor modern berbagi representasi data titik mengambang yang sama,
yaitu yang ditetapkan dalam standar IEEE 754-1985. (untuk beberapa diskusi prosesor tidak
standar, lihat 22.2.) Untuk nilai mengadakan 32-bit, eksponen direpresentasikan dalam 8 bit
(dengan e = 127), mantisa di 23; untuk 64-bit nilai ganda, eksponen adalah 11 bit (dengan e =
1.023), mantisa, 52. Sebuah trik tambahan digunakan untuk mantissa selalu satu, bit mantisa

disimpan dipandang sebagai yang didahului oleh "momok" potongan kecil dengan nilai 1.
dengan kata lain, mantisa M memiliki nilai 1.F numerik, dimana F (disebut fraksi) terdiri dari
potongan kecil (23 atau 25 jumlahnya) yang benar benar disimpan. Trik ini keuntungan sedikit
"sedikit" presisi.
Berikut adalah beberapa contoh dari IEEE 754 representasi dari nilai nilai ganda:

Anda dapat memeriksa representasi dari nilai apapun dengan kode seperti ini:
union Udoub {
double d;
unsigned char c [8]
void main () {
Udoub u;
u.d = 6.5;
for (int i=7; i>=0;i--) printf (%0,2x, u.c [i]);
printf (/n)
}
Ini adalah C, dan memprotes corak mode, tetapi akan kata. Pada kebanyakan prosesor,
termasuk Intel Pentium merupakan penerus, Anda akan mendapatkan hasil yang dicetak
401a000000000000, yang (menuliskan setiap hex angka empat angka biner) adalah garis las
dalam persamaan (1.1.2). jika Anda mendapatkan byte (kelompok dua angka hex) dalam urutan
terbalik, maka Anda prosesor adalah besar akhiran bukan sedikit kecil akhiran: The IEEE 754
standar tidak menentukan (atau perawatan) yang memesan byte dalam nilai pengembangan titik
adalah disimpan.

IEEE 754 standar termasuk representasi positif dan negatif tak terhingga, nol positif dan
negatif (diperlakukan sebagai setara komputasi, tentu saja), dan juga NaN ("bukan bilangan").
Tabel di halaman sebelumnya memberikan detail dari bagaimana diwakili.
Alasan mewakili beberapa nilai bentuk tidak normal, seperti yang ditunjukkan dalam
tabel adalah tomake "underflow ke nol" lebih anggun. Untuk urutan nilai nilai yang lebih kecil
dan lebih kecil, setelah Anda melewati nilai normalizable terkecil (dengan besarnya 2-127 atau 21023
; lihat tabel). Anda mulai benar menggeser potongan kecil terkemuka mantisa. Meskipun anda
secara bertahap kehilangan presisi, Anda tidak benar benar underflow ke nol sampai 23 atau 25
bit kemudian.
Ketika kebutuhan rutin untuk mengetahui sifat dari representasi pengambangan titik,
dapat referensi kelas numeric_limits, yang merupakan bagian dari Standar Perpustakaan C ++.
Misalnya, numeric_limits <double>:: min () mengembalikan nilai ganda terkecil normalisasi,
biasanya

. Untuk lebih lanjut tentang ini, lihat 22.2

1.1.2 Kesalahan pembulatan


Aritmatika antara angka dalam representasi pengambangan titik tidak tepat, bahkan jika
operan kebetulan persis diwakili (yaitu nilai-nilai yang tepat dalam bentuk persamaan 1.1.1).
Sebagai contoh, dua angka floating ditambahkan oleh pertama kanan pergeseran (membagi oleh
dua) mantissa yang lebih kecil (besarnya) satu dan sekaligus meningkatkan eksponen sampai dua
operan memiliki eksponen yang sama. Urutan bit rendah (paling signifikan) dari operan yang
lebih kecil hilang oleh pergeseran ini. Jika dua operan berbeda terlalu jauh dalam besarnya, maka
operan kecil secara efektif diganti dengan nol, karena yang benar bergeser untuk dilupakan.
Terkecil (besarnya) angka pengembangan titik yang, ketika ditambahkan ke
pengembangan titik nomor 1.0, menghasilkan hasil pengembangan titik yang berbeda dari 1,0
disebut, akurasi mesin m. IEEE 754 pelampung standar memiliki m sekitar
sementara ganda memiliki sekitar

. Nilai seperti ini dapat diakses sebagai.

misalnya, numeric_limits <double>:: min (). (Sebuah diskusi yang lebih rinci karakteristik mesin
di 22.2.) Secara kasar, akurasi mesin m adalah akurasi pecahan yang pengembangan titik
diwakili, sesuai dengan perubahan satu potongan kecil signifikan terkecil mantisa. Hampir
semua operasi aritmatika antara angka pengembangan harus dianggap sebagai memperkenalkan
kesalahan pecahan tambahan minimal m. Jenis kesalahan disebut kesalahan pembulatan.
Jika penting untuk memahami bahwa Em tidak terkecil angka pengembangan titik yang
dapat diwakili pada mesin. Angka itu tergantung pada berapa banyak potongan kecil yang ada di
eksponen, sementara m tergantung pada berapa banyak potongan kecil yang ada di mantisa.

Kesalahan pembulatan menumpuk dengan peningkatan jumlah perhitungan. Jika, dalam


perjalanan mendapatkan nilai yang dihitung, Anda melakukan N operasi aritmatika tersebut,
Anda mungkin sangat beruntung untuk memiliki total kesalahan kesalahan pembulatan pada
urutan

, jika kesalahan pembulatan datang secara acak atas atau bawah. (Akar kuadrat

berasal dari secara acak cara berjalan). Namun, perkiraan ini bisa sangat buruk melenceng karena
dua alasan:
(1) sangat sering terjadi bahwa keteraturan perhitungan Anda, atau kekhasan komputer
Anda, menyebabkan kesalahan pembulatan menumpuk istimewa dalam satu arah. Dalam hal ini
total akan urutan m
(2) beberapa kejadian yang tidak menguntungkan terutama dapat sangat meningkatkan
kesalahan putaran pembulatan operasi tunggal. Umumnya ini dapat ditelusuri ke pengurangan
dua angka yang sangat hampir sama, memberikan hasil yang hanya bit signifikan adalah mereka
(beberapa) yang urutan rendah yang operan berbeda. Anda mungkin berpikir bahwa seperti
"kebetulan" pengurangan tidak mungkin terjadi. Tidak selalu begitu. Beberapa ekspresi
matematika memperbesar probabilitas terjadinya sangat. Sebagai contoh, dalam rumus akrab
untuk solusi dari persamaan kuadrat.\

Selain menjadi halus dan pembulatan rawan bila b > 0 dan

(5.6 dalam kita

akan belajar bagaimana untuk menghindari masalah dalam kasus ini).


1.1.3 Kesalahan Pemotongan
Karakteristik kesalahan pembulatan dari perangkat keras komputer. Ini adalah satu lagi,
berbeda, jenis kesalahan yang merupakan karakteristik dari program algoritma yang digunakan,
independen dari hardware yang program dijalankan. Banyak algoritma numerik menghitung
"diskrit" perkiraan untuk beberapa diinginkan "terus menerus" kuantitas. Sebagai contoh, sebuah
terpisahkan dievaluasi secara numerik dengan menghitung fungsi di satu kumpulan diskrit titik,
bukan di "setiap" titik. Atau, fungsi dapat dievaluasi dengan menjumlahkan jumlah terbatas
istilah terkemuka di seri terbatas, daripada semua hal yang tak terbatas. Dalam kasus seperti ini,
ada sebuah parameter disesuaikan, misalnya, jumlah titik atau istilah, seperti bahwa "benar"
jawaban diperoleh hanya ketika parameter yang masuk ke tak terbatas. Setiap perhitungan
praktis dilakukan dengan terbatas, tapi cukup besar, pilihan parameter itu.
Perbedaan antara jawaban yang benar dan jawaban yang diperoleh dalam perhitungan
praktis disebut kesalahan pemotongan. Kesalahan pemotongan akan bertahan bahkan pada
hipotetis, "sempurna" komputer yang memiliki representasi jauh akurat dan tidak ada kesalahan

pembulatan. Sebagai aturan umum tidak ada banyak yang programmer dapat melakukan tentang
kesalahan pembulatan, selain untuk memilih algoritma yang tidak memperbesar itu tidak perlu
(lihat pembahasan "stabilitas" di bawah). Kesalahan pemotongan, di sisi lain, adalah sepenuhnya
di bawah kendali programmer. Bahkan, itu hanya sedikit berlebihan untuk mengatakan bahwa
minimalisasi pintar kesalahan pemotongan praktis seluruh isi bidang analisis numerik!
Sebagian besar waktu, kesalahan pemotongan dan kesalahan pembulatan tidak kuat
berinteraksi satu sama lain. Perhitungan A dapat dibayangkan sebagai memiliki, pertama,
kesalahan pemotongan yang akan ada jika dijalankan pada komputer presisi yang tak terbatas.
"tambah" kesalahan pembulatan terkait dengan jumlah operasi yang dilakukan.
1.1.4 Stabilitas
Kadang kadang metode lain yang menarik bisa menjadi tidak stabil. Ini berarti bahwa
setiap kesalahan pembulatan yang menjadi "dicampur ke" perhitungan pada tahap awal adalah
berturut turut diperbesar sampai datang ke mengisi jawaban yang benar. Sebuah metode yang
tidak stabil akan berguna pada hipotetis, komputer yang sempurna: tapi di dunia ini tidak
sempurna itu perlu bagi kita untuk mengharuskan algoritma stabil atau jika tidak stabil yang kita
gunakan mereka dengan hati hati.
Berikut ini adalah sederhana, jika agak buatan, contoh algoritma yang tidak stabil:
Misalkan diinginkan untuk menghitung semua kekuatan bilangan bulat yang disebut "Cara yang
Sangat Baik" nomor yang diberikan oleh

Ini berubah (Anda dapat dengan mudah memverifikasi) bahwa kekuatan n memenuhi rekursi
hubungan sederhana,
n+1 = n-1 n
Dengan demikian, mengetahui dua nilai pertama 0 = 1 dan 1 = 0,61803398, kita berturut
turut dapat menerapkan (1.1.5) melakukan hanya pengurangan tunggal, daripada perkalian
lambat oleh , pada setiap tahap.
Sayangnya, kekambuhan (1.1.5) juga memiliki solusi lain, yaitu nilai

Karena keadaan kambun adalah linear, dan karena solusi yang tidak diinginkan ini memiliki
besarnya lebih besar daripada satu, setiap campuran kecil itu diperkenalkan oleh kesalahan
pembulatan akan tumbuh secara eksponensial. Pada mesin yang khas, dengan menggunakan
pelampung 32-bit, (1.1.5) mulai memberikan jawaban benar salah sekitar n = 16, di mana titik n
turun hanya 10-4. Terulangnya (1.1.5) tidak stabil dan tidak dapat digunakan untuk tujuan lain.

Kami akan berjumpa dengan pertanyaan stabilitas dalam banyak samaran lebih canggih
dalam buku ini.

1.2 C Keluarga Syntax


Tidak hanya C ++, tetapi juga Java, C #, dan (untuk berbagai tingkat) bahasa komputer
lainnya, berbagi banyak sintaks skala kecil dengan bahasa perintah C. Dengan skala kecil, kita
berarti operasi pada built-in tipe, ekspresi sederhana, struktur kontrol, dan sejenisnya. Pada
bagian ini, kita meninjau beberapa dasar-dasar, memberikan beberapa petunjuk tentang
pemrograman yang baik, dan menyebutkan beberapa konvensi kami sebuah kebiasaan.
1.2.1 Operator
Sepotong pertama saran mungkin tampak berlebihan jika tidak begitu sering diabaikan:
Anda harus mempelajari semua operator C dan didahulukan dan associativity aturan mereka.
Anda tidak mungkin sendiri ingin menulis.
n << 1 1
Sebagai sinonim untuk 2 * n + 1 (untuk bilangan bulat positif n), tetapi Anda pasti perlu untuk
dapat melihat sekilas itu.
n << 1 + 1
Dalam sekali tidak hal yang sama! Silakan mempelajari tabel di atas meja di halaman
berikutnya saat Anda menyikat gigi setiap malam. Sedangkan set sesekali kurung yang tidak
perlu, untuk kejelasan, hampir tidak dosa, kode yang biasa lebih kurung menjengkelkan dan sulit
untuk dibaca.

Operator Precedence and Associativity Rules in C and C++


::
cakupan resolusi
Kiri ke kanan
()
memanggil fungsi
Kiri ke kanan
[]
susunan elemen (penanda)
.
seleksi member
->
seleksi member (dengan penunjuk)
++
pasca kenaikan
kanan ke kiri
-pasca penurunan
!
tanda logika
kanan ke kiri
~
pelengkat bitwise
penguranga unary
++
sebelum kenaikan
-sebelum pengurangan
&
alamat
*
isi dari (pembeda)
new
membuat baru
delete menghancurkan
(type) mengetik
sizeof ukuran dalam bytes
Kiri ke kanan
*
perkalian
/
pembagian
%
sisa
+
penjumlahan
Kiri ke kanan
pengurangan
<<
menggeser bitwise ke kiri
Kiri ke kanan
>>
menggeser bitwise ke kanan
<
operator aritmatika kurang dari
Kiri ke kanan
>
operator artmatika levih dari
<=
kurang dari sama dengan
>=
lebih dari sama dengan
==
operator aritmatika sama dengan
Kiri ke kanan
!=
operator aritmatika tidak sama dengan
&
tanda dan
Kiri ke kanan
^
tanda ekslusif atau
Kiri ke kanan
|
tanda atau
Kiri ke kanan
&&
logika dan
Kiri ke kanan
||
logika atau
Kiri ke kanan
?:
ekspresi kondisional
kanan ke kiri
=
operator penugasan
kanan ke kiri
juga += -= *= /= %=
<<= >>= &= ^= |=
,
ekspresi berurutan
Kiri ke kanan

1.2.2

Struktur

Pengendalian
Iterasi.
keluarga

Dalam

bahasa

C,

iterasi sederhana dapat


dilakukan
sebuah

dengan
for.

putaran

Contohnya
for
(j=2;j<=1000;j++)
{
b[j]=a[j-1] ;
a[j-1]=j ;
}
Hal

ini

konvensional

untuk

mengidentasi blok kode


yang

ditindaklanjuti

oleh

struktur

pengendalian,

dengan

membiarkan struktur itu


sendiri

dijorokkan.

Kami
menempatkan

ingin
kurung

kurawal pada baris yang

sama dengan pernyataan for , bukan pada baris berikutnya. Ini menghemat garis penuh di ruang
putih,.
Kondisional. Kondisional atau struktur if terlihat, pada umumnya, seperti ini:
If(...) {
...
}
Else if (...) {
...
}
Else{
...
}
Namun, karena pernyataan gabungan, kurung kurawal diperlukan hanya jika ada lebih
dari satu pernyataan dalam sebuah blok, konstruksi if bisa jadi kurang eksplisit dari yang
ditunjukkan di atas. Beberapa perhatian harus diberikan dalam membangun kumpulan klausa
if . Sebagai contoh, pertimbangkan hal berikut
if (b>3)
if (a>3) b+=1;
else b-=1;

/*questionable*/

Sebagaimana dinilai oleh identasi yang digunakan pada baris berturut-turut, maksud dari
penulis kode ini adalah sebagai berikut: 'Jika b lebih besar dari 3 a lebih besar dari 3, maka
kenaikan b. Jika b tidak lebih besar dari 3, maka pengurangan b. 'Sesuai aturan, namun, arti
sebenarnya adalah' jika b lebih besar dari 3, kemudian mengevaluasi. jika a lebih besar dari 3,
maka kenaikan b, dan jika kurang dari atau sama dengan 3, penurunan oleh b. 'Intinya adalah

dari sebuah klausa else adalah berkaitan dengan pernyataan terbuka if yang terbaru, tidak
peduli bagaimana Anda meletakkannya di luar halaman. Kebingungan dalam mengartikan
mudah diselesaikan dengan masuknya tanda kurung

yang memperjelas maksud Anda dan

meningkatkan program. Fragmen di atas harus ditulis sebagai:


If (b>3) {
If (a>3) b+=;
} else {
b-=1;
}

Iterasi while. Alternatif untuk iterasi for adalah struktur while, misalnya:
While (n<1000{
N *= 2;
J += 1;
}
Klausa kontrol (dalam hal ini n <1000) dievaluasi sebelum setiap iterasi. Jika klausa tidak
benar, pernyataan tertutup tidak akan dieksekusi. Secara khusus, jika kode ini ditemui pada saat n
lebih besar dari atau sama dengan 1000, pernyataan tidak akan dieksekusi sama sekali.
Iterasi Do-While. Pendamping iterasi while adalah struktur pengendalian terkait yang
menguji klausa kontrol pada akhir setiap iterasi:
Do{
N *= 2;
J+= 1;
} while (n< 1000);

Dalam hal ini, laporan tertutup akan dilaksanakan setidaknya sekali, terlepas dari nilai
awal n.
Break dan Continue. Anda menggunakan pernyataan break ketika Anda memiliki
putaran yang harus diulang beberapa kali sampai beberapa kondisi diuji di suatu tempat di
pertengahan putaran (dan mungkin diuji di lebih dari satu tempat) menjadi benar. Pada saat itu
Anda ingin keluar dari putaran dan melanjutkan dengan apa yang terjadi setelah itu. Dalam
keluarga bahasa C, pernyataan sederhana break mengakhiri pelaksanaan konstruksi for,
while, do, atau switch dan menghasilkan untuk instruksi sekuensial berikutnya. Sebuah
penggunaan khas mungkin
For(;;) {
...

(statement before the test)

If (...) break
...

(statement after the test)

}
...

(next sequential instruction)


Pasangan break adalah continue, yang mentransfer kontrol program hingga ke bagian

terakhir dari penutup kecil pernyataan for, while, atau do, tetapi hanya di dalam yang
mengakhiri badan pernyataan dengan kurung kurawal. Secara umum, solusi ini digunakan pada
test putaran berikutnya yang berhubungan dengan badan pernyataan.
1.2.3 Bagaimana Tipuan adalah Terlalu Menipu?
Setiap programmer kadang-kadang tergoda untuk menulis sebuah garis atau dua kode
yang begitu sangat rumit bahwa semua yang membacanya akan berdiri kagum karena
intelegensia penulisnya. Keadilan puitis adalah bahwa hal itu biasanya programmer yang sama
yang akan bingung, nantinya, mencoba memahami ciptaan sendiri. Anda mungkin sejenak akan
bangga atas diri Anda saat menulis satu baris ini
K=(-j)*(1+3*j)/2;

Jika Anda ingin menukar urutan siklis salah satu nilai j = (0,1,2) ke k = (1,2,0). Anda
akan menyesal kemudian, namun, lebih baik, dan kemungkinan juga lebih cepat, adalah
K=j+1;
If(k==3) k=0;
Di sisi lain, hal itu juga bisa menjadi kesalahan, atau setidaknya suboptimal. Terlalu
plossingly literal, seperti dalam
Switch(j){
Case0:k=1; break;
Case1:k=2; break;
Case2:k=0; break;
Default: {
Cerr << unexpected value for j;
Exit (1);
}
}
Ini (atau serupa) mungkin gaya rumahan jika Anda adalah salah satu dari 10 5 programmer
yang bekerja untuk perusahaan besar, tetapi jika Anda pemrogramer untuk penelitian Anda
sendiri, atau dalam kolaborasi dari kelompok kecil, jenis gaya anda akan segera menyebabkan
Anda kehilangan hutan untuk pohon. Anda perlu menemukan keseimbangan yang tepat antara
tipuan yang kabur dan hal boring yang jelas merupakan hal bertele-tele. Aturan yang baik adalah
bahwa Anda harus selalu menulis kode yang sedikit kurang rumit daripada yang Anda bersedia
untuk membaca, tapi hanya sedikit.

Ada garis tipis antara menjadi rumit (buruk) dan menjadi idiomatic (baik). Idiom adalah
ekspresi pendek yang cukup umum, atau cukup cukup jelas, yang dapat Anda gunakan secara
bebas. Sebagai contoh, pengujian bilangan bulat n adalah bilangan genap atau ganjil oleh
If(n&1)...
Apakah, kita berpikir, jauh lebih baik untuk
If(n%2==)...
Kami sama ingin menggandakan bilangan bulat positif dengan menulis
N<<=1
Atau mengkonstruksi kurung dengan menulis
(1 << n) - 1
Dan seterusnya
Beberapa idiom yang patut dipertimbangkan bahkan ketika mereka tidak begitu segera
jelas. SES Anderson {2} telah mengumpulkan sejumlah "bit-twiddling hacks," dari mana kita
menunjukkan tiga di sini:
If ((v&(v-1))==0) {}

is a power of 2 or zero.

Tes apakah v adalah pagkat dari 2. Jika Anda peduli tentang kasus v = 0. Anda harus
menulis
If(v&&(v&(v-1))==0)) {}

is a power of 2

Idiom
For (c=0;v;c++) v&=v-1;
yang memberikan sebagai c jumlah set (= 1) bit dalam bilangan bulat v positif atau sebarang
bilangan bulat (menghancurkan v dalam proses). Jumlah iterasi hanya sebanyak jumlah bit-bit.
Idiom

v--;
v|v>>1;v|=v>>2;v|=v>>4;v|=v>>8;v|=v>>16;
v++;
kumpulan sebuah nilangan positif (atau sebarang) 32-bit bilangan bulat v hingga pangkat
berikutnya dari 2 yang v.
Ketika menggunakan hacks bit-twiddling hacks, kita akan mencakup komentar jelas
dalam kode.
1.2.4 Utilitas Macro atau Templated Fungsi
nr3. h meliputi, antara lain, definisi untuk fungsi
MAX(a,b)
MIN(a,b)
SWAP(a,b)
SIGN(a,b)
Ini semua adalah cukup jelas, kecuali mungkin yang terakhir. TANDA (a, b)
mengembalikan nilai dengan besarnya sama dan tanda yang sama seperti b. fungsi-fungsi ini
semua diimplementasikan sebagai fungsi inline templated, sehingga mereka dapat digunakan
untuk semua jenis argumen yang masuk akal semantis. Implementasi sebagai macro juga
mungkin.

1.3 Objek, Kelas, dan Warisan


Sebuah objek atau kelas (istilah yang anterchangeable) adalah struktur program yang
mengelompokkan bersama-sama beberapa variabel, atau fungsi, atau keduanya, n sedemikian
rupa sehingga semua variabel atau fungsi termasuk "melihat" satu sama lain dan dapat
berinteraksi secara intim, sementara sebagian besar struktur internal ini tersembunyi dari struktur
program lain dan unit. Objek memungkinkan pemrograman berorientasi pada objek (OOP),
yang telah diakui sebagai paradigma sukses yang unik untuk menciptakan perangkat lunak yang

kompleks. Patut diketahui OOP adalah bahwa obyek memiliki keadaan dan perilaku. Keadaan
objek yang dilukiskan oleh nilai-nilai yang tersimpan dalam variabel anggotanya. Sedangkan
perilaku yang mungkin ditentukan oleh fungsi anggota. Kami akan menggunakan objek dalam
cara lain juga
Terminologi di sekitar OOP dapat membingungkan. objek, kelas, dan struktur cukup
banyak merujuk untuk hal yang sama. Fungsi anggota di kelas sering dirujuk sebagai metode
milik kelas itu. Dalam C ++, objek didefinisikan dengan kata kunci class atau kata struct. Ini
berbeda, namun, dalam rincian tentang bagaimana ketatnya mereka menyembunyikan internal
objek dari pandangan publik. Secara khusus,
Struct SomeName {...
Didefiisikan menjadi sama dengan
Class SomeName {
Public : ...
Dalam buku ini kita selalu menggunakan struct. Ini bukan karena kita mencela
penggunaan sesifikasi akses public dan private di OOP, tetapi hanya karena kontrol akses
tersebut akan menambah sedikit untuk memahami metode numerik yang mendasari fokus dari
buku ini. Bahkan, penspesifikasi akses bisa menghambat pemahaman Anda, karena Anda akan
terus menggerakkan dari pribadi ke publik (dan kembali lagi) sebagai program uji kasus Anda
yang berbeda dan ingin memeriksa yang berbeda di dalamnya, biasanya, swasta, variabel.
Karena kelas kami dinyatakan oleh struct,bukan kelas, penggunaan kata "kelas"
berpotensi membingungkan, dan kami akan selalu mencoba untuk menghindarinya. Jadi "subjek"
berarti struct, yang benar-benar sebuah kelas.
Jika Anda adalah seorang pemula OOP, adalah penting untuk memahami perbedaan
antara mendefinisikan obyek dan menginstansinya. Anda mendefinisikan suatu objek dengan
menulis kode seperti ini
Struct Twovar {

Doub a,b;
Twovar(const Doub aa, const Doub bb) : a(aa) , b(bb) {}
Doub sum() {return a+b;}
Doub diff() {return a-b}
};
Kode ini tidak menciptakan objek Twovar. Ini hanya memberitahu komplier cara
membuat suatu

ketika, kemudian dalam program Anda, Anda mengatakan itu untuk

melakukannya, misalnya dengan deklarasi seperti


Twovar mytwovar(3.,5.);
Yang memanggil konstruktor Twovar dan menciptakan sebuah instansi dari (atau
beberapan instansi) sebuah Twovar. Dalam contoh ini, konstruktor juga menetapkan variabel
internal a dan b untuk 3 dan 5, masing-masing. Anda dapat memasukkan sejumlah persamaan
yang ada, noninteracting, contoh:
Twovar anothertwovar(4.,6);
Twovar athirdtwovar(7.,8.);
1.3.1 Penggunaan Sederhana Objects
Kami menggunakan objek dengan berbagai cara, mulai dari yang sepele hingga
kompleks, tergantung pada kebutuhan metode numerik khusus yang sedang dibahas. Seperti
disebutkan dalam 1.0, kurangnya konsistensi berarti bahwa Numerical Recipes bukan contoh
yang berguna dari kumpulan program (atau, dalam konteks OOP, kumpulan kelas). Ini juga
berarti bahwa, di suatu tempat di buku ini, Anda mungkin dapat menemukan contoh dari setiap
cara yang mungkin untuk berpikir tentang obyek dalam komputasi numerik!
Objek untuk mengelompokkan fungsi. Kadang-kadang sebuah objek hanya
mengumpulkan bersama sekelompok fungsi terkait erat, tidak terlalu berbeda dari cara yang

mungkin Anda menggunakan sebuah namespace. Misalnya, penyederhanaan Cahpter 6 ini


objek Erf terlihat seperti:
Struct Erf {
Doub erf (Doub x);
Doub erfc (Doub x);
Doub inverf (Doub p);
Doub inverfc (doub p);
Doub erfccheb(Doub z);
};
Seperti yang akan dijelaskan dalam 6.2, pertama empat metode yang dimaksudkan untuk
dipanggil oleh pengguna, memberikan fungsi error, kesalahan fungsi komplementer, dan dua
sesuai fungsi invers. Tapi metode ini terbagi beberapa kode dan juga menggunakan kode umum
dalam metode terakhir, erfccheb, yang pengguna biasanya akan mengabaikan sepenuhnya. Oleh
karena itu masuk akal untuk kelompok seluruh koleksi sebagai objek Erf. Mengenai satu-satunya
kelemahan dari ini adalah Anda harus memahami contoh objek Erf sebelum Anda dapat
menggunakan (katakanlah) fungsi erf:
Erf myerf; The name myerf is arbitrary.
...
Doub y = myerf.erf(3.);
Memahami contoh objek tidak benar-benar menyelesaikan apapun di sini, karena tidak
mengandung variabel Erf (yaitu, tidak memiliki keadaan tersimpan). Itu hanya memberitahu
kompilator apa nama lokal yang Anda akan gunakan dalam mengacu pada fungsi anggotanya.
(Kami biasanya menggunakan nama erf untuk contoh Erf, tapi kami pikir bahwa erf.erf (3.) Akan
membingungkan dalam contoh di atas.)
Objek untuk Standarisasi sebuah Interface. Dalam 6.14 kita akan membahas sejumlah
distribusi probabilitas standar misalnya, normal, Cauchy, binomial, Poisson, dll Masing-masing
mendapatkan definisi objek tersendiri, contoh,
struct Cauchydist {

Doub mu, sig;


Cauchydist(Doub mmu = 0., Doub ssig = 1.) : mu(mmu), sig(ssig) {}
Doub p(Doub x);
Doub cdf(Doub x);
Doub invcdf(Doub p);
};
Dimana fungsi p mengembalikan kepadatan probabilitas, fungsi cdf mengembalikan fungsi
kumulatif distribusi (cdf), dan fungsi invcdf mengembalikan kebalikan dari cdf tersebut. Karena
interface konsisten di semua distribusi probabilitas yang berbeda, Anda dapat mengubah yang
distribusi program menggunakan dengan mengubah baris program tunggal, misalnya dari
Cauchydist mydist();
to
Normaldist mydist();
Semua referensi berikutnya untuk fungsi seperti mydist.p, mydist.cdf, dan sebagainya, sehingga
berubah secara otomatis. Hal ini tidak menghampiri OOP sama sekali, tapi bisa sangat mudah.
Objek untuk Mengembalikan Nilai kelompok. Sering terjadi bahwa fungsi computes memiliki
manfaat lebih dari satu, tetapi Anda tidak tahu mana yang satu atau lebih sebenarnya tertarik
pada fungsi panggilan tertentu. Sebuah penggunaan yang mudah dari objek adalah untuk
menyimpan semua hasil berpotensi berguna dan kemudian membiarkan pengguna mengambil
pengguna yang menarik. Sebagai contoh, versi sederhana dari struktur Fitab dalam Bab 15, yang
cocok dengan garis lurus y D a bx C untuk satu set titik data xx dan yy, terlihat seperti ini:
struct Fitab {
Doub a, b;
Fitab(const VecDoub &xx, const VecDoub &yy); Constructor.
};
(Kita akan membahas VecDoub dan terkait hal di bawah ini, di 1.4.) Pengguna menghitung
kecocokan dengan memanggil konstruktor dengan titik data sebagai argumen,
Fitab myfit(xx,yy);

Kemudian kedua "jawaban" a dan b adalah secara terpisah tersedia sebagai myfit.a dan myfit.b.
Kita akan melihat contoh-contoh yang lebih rumit di seluruh buku.
Objek Yang Menghemat intern gabungan Beberapa Penggunaan. Ini adalah OOP lama,
layak disebutkan. Sebuah contoh yang baik adalah Bab 2 ini LUdcmp objek, yang (dalam bentuk
singkatan) terlihat seperti ini:
struct LUdcmp {
Int n;
MatDoub lu;
LUdcmp(const MatDoub &a); Constructor.
void solve(const VecDoub &b, VecDoub &x);
void inverse(MatDoub &ainv);
Doub det();
};
Objek ini digunakan untuk memecahkan persamaan linear dan / atau inversi matriks. Anda
menggunakannya dengan membuat sebuah contoh dengan matriks Anda sebagai argumen dalam
pembentuk. Konstruktor kemudian menghitung dan menyimpan, di lu matriks internal, disebut
LU dekomposisi matriks Anda (lihat 2.3). Biasanya Anda tidak akan menggunakan lu matriks
langsung (meskipun Anda bisa jika Anda ingin). Sebaliknya, Anda sekarang telah tersedia
metode memecahkan (), yang mengembalikan solusi vektor x untuk setiap kanan sisi b, terbalik
(), yang mengembalikan matriks inverse, dan det (), yang mengembalikan determinan dari
matriks Anda.
Anda dapat memanggil salah satu atau semua metode LUdcmp dalam urutan apapun; Anda
mungkin juga ingin melihat pemecahan beberapa kali, dengan sisi kanan yang berbeda. Jika
Anda memiliki lebih dari satu matriks dalam masalah Anda, Anda membuat contoh yang terpisah
dari LUdcmp untuk masing-masing, misalnya,
LUdcmp alu(a), aalu(aa);
setelah itu alu.solve () dan aalu.solve () adalah metode untuk memecahkan persamaan linear
untuk setiap masing-masing matriks dan aa; alu.det () dan aalu.det () kembali keduanya faktor
penentu; Dan seterusnya.
Kami tidak selesai daftar cara untuk menggunakan benda-benda: Beberapa lebih banyak dibahas
dalam beberapa bagian.

1.3.2 Aturan Scope dan Obyek Destruction


Contoh terakhir ini, LUdcmp, menimbulkan isu penting tentang bagaimana mengelola waktu
obyek dan penggunaan memori dalam program Anda.
Untuk matriks diperbesar, konstruktor LUdcmp melakukan banyak perhitungan. Anda memilih
dalam program Anda, Anda ingin hal ini terjadi dalam cara yang jelas, dengan mendeklarasi
LUdcmp alu (a);
hanya tempat itu. Perbedaan penting antara bahasa non-OOP (seperti C) dan bahasa OOP (seperti
C ++) adalah bahwa, dalam terakhir, deklarasi tidak petunjuk pasif untuk compiler, tetapi
statments dieksekusi pada run-time.
The LUdcmp konstruktor juga, untuk matriks besar, meraih banyak memori, untuk menyimpan
lu matriks. Bagaimana Anda mengambil alih ini? Yaitu, bagaimana Anda berkomunikasi bahwa
itu harus menyelamatkan keadaan ini selama Anda mungkin perlu untuk panggilan ke metode
seperti alu.solve (), tapi tidak tanpa batas?
Jawabannya terletak pada ketat dan diprediksi aturan C ++ 's tentang ruang lingkup. Anda dapat
memulai lingkup sementara pada setiap titik dengan menulis tanda kurung buka, "{". Anda
mengakhiri lingkup dengan kurung tutup, "}". Anda dapat cakupan dengan cara yang jelas.
Benda yang dinyatakan dalam lingkup yang hancur (dan sumber daya memori mereka kembali)
ketika akhir cakupan tercapai. Sebuah contoh mungkin terlihat seperti ini:
MatDoub a(1000,1000);

Create a big matrix,

VecDoub b(1000),x(1000);

and a couple of vectors.

...
{

Begin temporary scope.

LUdcmp alu(a);

Create object alu.

...
alu.solve(b,x);

Use alu.

...
}

End temporary scope. Resources in alu are freed.

...
Doub d = alu.det();

ERROR! alu is out of scope.

Contoh ini mengasumsikan bahwa Anda memiliki beberapa penggunaan lainnya untuk matriks
nanti. Jika tidak, maka deklarasi harus sendirinya mungkin berada di dalam cakupan sementara.
Sadarilah bahwa semua blok program yang digambarkan oleh kurung adalah unit ruang lingkup.
Ini termasuk blok utama yang terkait dengan definisi fungsi dan juga blok terkait dengan struktur
kontrol. Dalam kode seperti ini,
for (;;) {
...
LUdcmp alu(a);
...
}
contoh baru dari alu dibuat pada setiap iterasi dan kemudian dihilangkan pada akhir iterasi itu.
Ini mungkin kadang-kadang apa yang Anda berniat (jika matriks perubahan pada setiap iterasi,
misalnya); tetapi Anda harus berhati-hati untuk tidak membiarkan hal itu terjadi tidak sengaja.

1.3.3 Fungsi dan Functors


Banyak cara dalam buku ini untuk mengambil fungsi sebagai masukan. Sebagai contoh,
kuadratur (integrasi) rutinitas dalam Bab 4 take sebagai masukan fungsi f .x / diintegrasikan.
Untuk kasus sederhana seperti f .x / D x2, Anda kode fungsi seperti hanya sebagai
Doub f(const Doub x) {
return x*x;
}
dan meneruskan f sebagai argumen untuk rutinitas. Namun, seringkali berguna untuk
menggunakan objek yang lebih umum untuk berkomunikasi fungsi yang rutin. Sebagai contoh,
f .x / tergantung pada variabel atau parameter yang perlu disampaikan dari program memanggil
lainnya. Atau perhitungan f .x / mungkin terkait dengan sub perhitungan atau informasi dari
bagian lain dari program lainnya. Dalam non-OOP pemrograman, komunikasi ini biasanya
dilakukan dengan variabel global yang lewat informasi "di atas kepala" dari rutinitas yang
menerima argumen fungsi f.
C ++ menyediakan solusi lebih baik dan lebih elegan: benda fungsi atau functors. Sebuah functor
hanyalah sebuah objek di mana operator () telah dipenuhi untuk memainkan peran

mengembalikan nilai fungsi. (Tidak ada hubungan antara penggunaan ini dari functor kata dan
arti yang berbeda dalam matematika murni.) Kasus f .x / D x2 sekarang akan dikodekan sebagai
struct Square {
Doub operator()(const Doub x) {
return x*x;
}
};
Untuk menggunakan ini dengan kuadratur atau rutin lainnya, Anda menyatakan sebuah contoh
Kuadrat
Square g;
dan meneruskan g ke rutinitas. Di dalam rutinitas kuadratur, sebuah sebutan g (x)
mengembalikan nilai fungsi dengan cara yang biasa.
Dalam contoh di atas, tidak ada gunanya menggunakan functor bukannya bidang fungsi. Tapi
misalkan Anda memiliki parameter dalam masalah, misalnya, f .x / D CXP, di mana c dan p
harus disampaikan dari tempat lain dalam program Anda. Anda dapat mengatur parameter
melalui konstruktor:
struct Contimespow {
Doub c,p;
Contimespow(const Doub cc, const Doub pp) : c(cc), p(pp) {}
Doub operator()(const Doub x) {
return c*pow(x,p);
}
};
Dalam memanggil program, Anda mungkin menyatakan contoh Contimespow oleh
Contimespow h (4., 0,5); mengkomunikasikan c dan p untuk functor dan kemudian lulus h ke
rutinitas. Jelas Anda dapat membuat functor yang jauh lebih rumit. Sebagai contoh, dapat berisi
fungsi pembantu lainnya untuk membantu dalam perhitungan nilai fungsi.
Jadi harus kita menerapkan semua cara kami untuk menerima functors saja dan tidak berfungsi?
Untungnya, kita tidak harus memutuskan. Kita bisa menulis cara sehingga mereka dapat

menerima baik fungsi atau functor. Rutinitas menerima hanya berfungsi untuk diintegrasikan dari
a ke b mungkin dinyatakan sebagai Doub someQuadrature (Doub func (const Doub), const Doub
sebuah, const Doub b);
Untuk memungkinkan untuk menerima baik fungsi atau functors, kita bukannya membuat fungsi
contoh:
template <class T>
Doub someQuadrature(T &func, const Doub a, const Doub b);
Sekarang angka kompilator tahu apakah Anda memanggil someQuadrature dengan fungsi atau
functor dan menghasilkan kode yang sesuai. Jika Anda memanggil rutin di satu tempat dalam
program Anda dengan fungsi dan di lain dengan functor, compiler akan menangani itu juga.
Kami akan menggunakan kemampuan ini untuk lulus functors sebagai argumen dalam banyak
lain
tempat di buku keberadaan argumen fungsi yang diperlukan. Ada keuntungan yang luar biasa
dalam fleksibilitas dan kemudahan penggunaan.
Sebagai konvensi, ketika kita menulis Ftor, kita artikan functor seperti Kuadrat atau
Contimespow atas; ketika kita menulis fbare, wemean sebuah "kosong" fungsi seperti f atas; dan
ketika kita menulis ftor (semua dalam huruf kecil), kita berarti Instansiasi functor, yaitu, sesuatu
yang dinyatakan seperti
Ftor ftor(...); Replace the dots by your parameters, if any.
Tentu saja nama mu untuk functors dan instantiations mereka akan berbeda.
Sedikit sintaks lebih rumit terlibat dalam melewati fungsi untuk objek yang contoh untuk
menerima baik fungsi atau functor. Jadi jika objek tersebut
template <class T>
struct SomeStruct {
SomeStruct(T &func, ...); constructor
...
kami akan memberi contoh dengan functor seperti ini:
Ftor ftor;
SomeStruct<Ftor> s(ftor, ...

tetapi dengan fungsi seperti ini:


SomeStruct<Doub (const Doub)> s(fbare, ...
Dalam contoh ini, fbare mengambil konstanta tunggal dua argumen dan mengembalikan ganda.
Anda harus menggunakan argumen dan tipe kembali untuk kasus tertentu, saja.
1.3.4 Inheritance
Objek bisa diartikan sebagai turunan dari yang lainnya, sudah ditetapkan, objek. Dalam
inheritance tersebut, "orang tua" kelas disebut kelas dasar, sedangkan "anak" kelas ini disebut
kelas turunan. Sebuah kelas turunan memiliki semua metode dan kumpulan disimpan dari kelas
dasar, ditambah dapat menambahkan yang baru.
"adalah-a" Hubungan. Penggunaan paling sederhana inheritance adalah untuk disebut
menggambarkan adalah suatu hubungan-. Teks OOP penuh dengan contoh keberadaan kelas
dasar Kebun Binatang Hewan dan kelas turunan adalah singa. Dalam kata lain, Singa "adalah-a"
ZooAnimal. Kelas dasar memiliki metode umum untuk semua Kebun binatang Hewan, misalnya
makan () dan tidur (), sedangkan kelas turunan meluas kelas dasar dengan metode tambahan
tertentu ke Lion, misalnya gemuruh () dan pemakan ().
Dalam buku ini kita gunakan adalah-inheritance lebih sering daripada yang mungkin Anda
harapkan. Kecuali dalam beberapa situasi sangat kaku, seperti kelas masing-masing matriks
dioptimalkan ("matriks segitiga adalah masing-masing matriks-a"), kami menemukan bahwa
keragaman tugas dalam komputasi ilmiah tidak meminjamkan dirinya ke ketat-a hierarki. Ada
pengecualian, namun. Misalnya, dalam Bab 7, kita mendefinisikan sebuah objek Ran dengan
metode untuk kembali menyimpang acak seragam dari berbagai jenis (misalnya, Int atau ganda).
Kemudian dalam bab ini, kita mendefinisikan objek untuk kembali jenis lain menyimpang acak,
misalnya normal atau binomial. Ini didefinisikan sebagai kelas turunan dari Ran, misalnya,
struct Binomialdev : Ran {};
sehingga mereka dapat membagi mesin yang sudah di Ran. Ini adalah benar sebuah hubungan,
karena "penyimpang binomial adalah sebuah penyimpang acak."
Contoh lain terjadi pada Bab 13, di mana objek Daub4, Daub4i, dan Daubs semuanya berasal
dari kelas dasar wavelet. Berikut Wavelet adalah kelas dasar abstrak atau ABC [1,4] yang tidak
memiliki konten sendiri. Sebaliknya, itu hanya menentukan interface untuk semua metode yang
setiap Wavelet diperlukan untuk memiliki. Hubungan ini tetap adalah sebuah : Daub4 adalah
sebuah Wavelet.
"Prasyarat" Hubungan. Bukan untuk alasan dogmatis, tetapi hanya karena lebih mudah, kita
sering menggunakan warisan untuk menyampaikan kepada obyek seperangkat metode yang
dibutuhkan sebagai prasyarat. Hal ini terutama terjadi ketika set yang sama prasyarat digunakan

oleh lebih dari satu objek. Dalam penggunaan ini warisan, kelas dasar tidak memiliki kesatuan
kebun binatang hewan tertentu; mungkin ambil tas. Tidak ada logis-hubungan antara dasar dan
kelas turunan.
Contoh dalam Bab 10 adalah Bracketmethod, yang merupakan kelas dasar untuk beberapa
rutinitas minimisasi, tetapi yang hanya menyediakan metode umum untuk bracketing awal
minimum. Dalam Bab 7, objek Hashtable menyediakan metode prasyarat untuk turunan kelas
Hash dan Mhash, tapi tidak bisa mengatakan, "Mhash adalah-Hashtable" dengan cara yang
berarti. Contoh ekstrim, dalam Bab 6, adalah kelas dasar Gauleg18, yang tidak apa-apa kecuali
menyediakan sekelompok konstanta untuk integrasi Gauss- Legendre untuk diturunkan kelas
Beta dan Gamma, yang keduanya membutuhkan mereka. Demikian pula, daftar panjang
konstanta disediakan untuk rutinitas StepperDopr853 dan StepperRoss dalam Bab 17 oleh kelas
dasar untuk menghindari kekacauan coding algoritma.
Abstraksi parsial. Warisan dapat digunakan dengan cara yang lebih rumit atau situationspecific.
Sebagai contoh, perhatikan Bab 4, di mana aturan quadrature dasar seperti Trapzd dan Midpnt
digunakan sebagai blok bangunan untuk membangun algoritma kuadratur lebih rumit. Fitur
kunci aturan-aturan sederhana berbagi adalah mekanisme untuk menambahkan lebih banyak poin
untuk sebuah pendekatan yang ada untuk integral untuk mendapatkan "berikutnya" tahap
penyempurnaan. Hal ini menunjukkan benda-benda ini berasal dari clase dasar abstrak yang
disebut Quadrature, yang menetapkan bahwa semua benda yang berasal dari itu harus memiliki
metode berikutnya (). Ini bukan spesifikasi lengkap umum adalah-antarmuka; itu abstrak hanya
satu fitur yang ternyata berguna.
Misalnya, dalam 4,6, yang Stiel keberatan memanggil, dalam situasi yang berbeda, dua benda
quadrature yang berbeda, Trapzd dan DErule. Ini tidak dipertukarkan. Mereka memiliki argumen
konstruktor yang berbeda dan tidak bisa dengan mudah baik dibuat ZooAnimals (seolah-olah).
Stiel tentu tahu tentang perbedaan mereka. Namun, salah satu metode Stiel ini, quad (), tidak
(dan tidak harus) tahu tentang perbedaan-perbedaan ini. Hanya menggunakan metode berikutnya
(), yang ada, dengan definisi yang berbeda, baik dalam Trapzd dan DErule.
Walaupun ada beberapa cara berbeda untuk menghadapi situasi seperti ini, sebuah EASYONE
tersedia sekali Trapzd dan DErule telah diberi abstrak kelas dasar Quadrature umum yang berisi
apa-apa kecuali antarmuka virtual untuk berikutnya. Dalam kasus seperti ini, kelas dasar adalah
fitur desain kecil sejauh pelaksanaan Stiel yang bersangkutan, hampir renungan, bukannya
puncak dari desain top-down. Selama penggunaan jelas, tidak ada yang salah dengan hal ini.
Bab 17, yang membahas persamaan diferensial biasa, memiliki beberapa bahkan lebih rumit
contoh yang menggabungkan turunan dan template. Kami menunda pembahasan lebih lanjut
untuk ada.
REFERENSI PUSTAKA DAN BACAAN LEBIH LANJUT:

Stroustrup, B. 1997, The C++ Programming Language, 3rd ed. (Reading, MA: AddisonWesley).[1]
Lippman, S.B., Lajoie, J., and Moo, B.E. 2005, C++ Primer, 4th ed. (Boston: Addison-Wesley).
[2] Keogh, J., and Giannini, M. 2004, OOP Demystified (Emeryville, CA: McGrawHill/Osborne).[3] Cline, M., Lomow, G., and Girou, M. 1999, C++ FAQs, 2nd ed. (Boston:
Addison-Wesley).[4]

1.4 Objek Vektor dan Matriks


C ++ Standard Library [1] termasuk vektor sangat baik <> Template kelas. Tentang satu-satunya
kritik yang salah dapat membuat dari itu adalah bahwa itu begitu kaya fitur bahwa beberapa
compiler vendor mengabaikan untuk memeras sedikit terakhir kinerja dari operasi yang paling
dasar, misalnya kembali sebuah elemen dengan subscript-nya. Kinerja yang sangat penting
dalam aplikasi ilmiah; tidak adanya sesekali dalam kompiler C ++ adalah alasan utama bahwa
banyak ilmuwan masih (seperti yang kita menulis) program C, atau bahkan di Fortran!
Juga termasuk dalam ++ Standard Library C adalah valarray kelas <>. Pada suatu waktu,
ini seharusnya menjadi kelas vektor-seperti yang dioptimalkan untuk komputasi numerik,
termasuk beberapa fitur yang terkait dengan matriks dan array multidimensi. Namun, seperti
dilaporkan oleh salah satu peserta,
Kelas valarray tidak dirancang dengan baik. Bahkan, ada yang mencoba untuk
menentukan apakah spesifikasi akhir bekerja. Hal ini terjadi karena tidak ada yang merasa
"bertanggung jawab" untuk kelas ini. Orang-orang yang memperkenalkan valarrays ke
perpustakaan standar C ++ meninggalkan panitia lama sebelum standar itu selesai.
Hasil sejarah ini adalah bahwa C ++, setidaknya sekarang, memiliki baik (tapi tidak
selalu dapat diandalkan dioptimalkan) kelas untuk vektor dan tidak ada kelas diandalkan sama
sekali untuk matriks atau array dimensi yang lebih tinggi. Apa yang harus dilakukan? Kami akan
mengadopsi strategi yang menekankan fleksibilitas dan menganggap hanya seperangkat minimal
properti untuk vektor dan matriks. Kami kemudian akan memberikan kita sendiri, dasar, kelas
untuk vektor dan matriks. Untuk sebagian besar kompiler, ini setidaknya seefisien vector <> dan
vektor lainnya dan kelas matriks umum digunakan. Tetapi jika, untuk Anda, mereka tidak, maka
mudah untuk mengubah ke yang berbeda kelas, seperti yang kita akan menjelaskan.

1.4.1Typedef
Fleksibilitas dicapai dengan memiliki beberapa lapisan typedef jenis-tipuan, diselesaikan
pada waktu kompilasi sehingga tidak ada run-time hukuman kinerja. Tingkat pertama dari jenistipuan, tidak hanya untuk vektor dan matrik tapi untuk hampir semua variabel, adalah bahwa kita
menggunakan nama jenis yang ditetapkan pengguna, bukan jenis yang mendasar C ++. Ini
didefinisikan dalam nr3.h. Jika Anda pernah menemukan sebuah compiler dengan aneh dibangun
di jenis, definisi ini adalah "hook" untuk membuat perubahan yang diperlukan. Daftar lengkap
definisi tersebut
NR Type

Usual Definition

Intens

Char

Char

8-bit signed integer

Uchar

unsigned

8-bit unsigned integer

Int

int

32-bit signed integer

Uint

unsigned int

32-bit unsigned integer

Llong

long long int

64-bit signed integer

Ullong

unsigned long long int

64-bit unsigned integer

Doub

double

64-bit floating point

Ldoub

long double

[reserved for future use]

Complex

complex <double>

2 x64-bit floating complex

Bool

bool

True or false

Contoh ketika Anda mungkin perlu mengubah typedef di nr3.h adalah jika compiler Anda int
tidak

32

bit,

atau

jika

tidak

mengenali

jenis

int

panjang.

Anda mungkin perlu mengganti jenis-vendor tertentu seperti (dalam kasus Microsoft) -int32 dan
-int64.

Tingkat kedua tipe-tipuan mengembalikan kita ke pembahasan vektor dan matriks. Vektor
dan matriks jenis yang muncul di Numerical Recipes kode sumber adalah sebagai berikut.
Vektor: VecInt, VecUnit, VecChar, VecUchar, VecCharp, VecLlong, VecUllong, VecDoubp,
VecComplex, dan VecBool. Matriks: MatInt, MatUnit, MatChar, MatUchar, MatCharp,
MatLlong, MatUllong, MatDoubp, MatComplex, dan MatBool. Ini semua harus dimengerti,
semantik, sebagai vektor dan matriks yang elemen yang jenis yang ditetapkan pengguna yang
sesuai, di atas. Mereka berakhiran "p" memiliki unsur-unsur yang pointer, misalnya, VecCharp
adalah vektor dari pointer ke char, yaitu char *. Jika Anda bertanya-tanya mengapa diatas tidak
combinatorially lengkap, itu karena kita tidak terjadi untuk menggunakan semua kemungkinan
kombinasi dari vec. Mat, jenis fundamental, dan pointer dalam buku ini. Anda dapat
menambahkan jenis lanjut analog yang Anda butuhkan mereka
Tunggu, masih ada lagi! Untuk setiap vektor dan matriks ketik di atas, kami juga
mendefinisikan jenis dengan nama yang sama ditambah satu dari akhiran "_I", "_0", dan "_I0",
misalnya VecDoub_I0. Kami menggunakan ini jenis bersufiks untuk menentukan argumen dalam
definisi fungsi. Artinya, masing-masing, adalah bahwa argumen adalah "masukan", "output",
atau "baik input dan output". * Jenis _I secara otomatis ditetapkan menjadi const. Kami
membicarakan hal ini lebih lanjut dalam 1.5.2 di bawah topik kebenaran const.
Ini mungkin tampak berubah-ubah bagi kita untuk mendefinisikan seperti daftar panjang
jenis ketika sejumlah jauh lebih kecil dari jenis template yang akan dilakukan. Alasannya adalah
fleksibilitas: Anda memiliki hook ke mendefinisikan ulang setiap salah satu jenis individual,
sesuai dengan kebutuhan Anda untuk efisiensi program, standar lokal, const-benar, atau apa pun.
Bahkan, di nr3.h, semua jenis ini typedef'd satu vektor dan satu kelas matriks, sepanjang baris
berikut:
typedef NRvector <Int> VecInt, VecInt_0, VecInt_I0;
...
typedef NRmatrix <Int> MatInt, MatInt_0, MatInt_I0;
typedf const Nmatrixr <Int> MatInt_I; typedf const NRvector <Int> VecInt_I;
...
typedef NRvector <Doub> VecDoub, VecDoub_0, VecDoub_I0;
typedf const NRvector <Doub> VecDoub_I;
...
typedef NRmatrix <Doub> MatDoub, MatDoub_0, MatDoub_I0;
typedf const NRmatrix <Doub> MatDoub_I;

Jadi (fleksibilitas, lagi) Anda dapat mengubah definisi satu jenis tertentu, seperti VecDoub, atau
Anda dapat mengubah pelaksanaan semua vektor dengan mengubah definisi NRvector <>. Atau,
Anda hanya dapat meninggalkan hal cara kita memiliki mereka di nr3.h. Yang seharusnya
bekerja dengan baik di 99,9% dari semua aplikasi.
1.4.2 Metode diperlukan untuk Vector dan Kelas Matrix
Hal penting tentang vektor dan matriks kelas tidak apa nama mereka typedef'd, tapi
metode apa yang diasumsikan untuk mereka (dan disediakan dalam NRvector dan NRmatrix
kelas template). Untuk vektor, metode yang diasumsikan adalah bagian dari orang-orang di C ++
Standar Library vector <> kelas. Jika v adalah vektor tipe NRvector maka kita asumsikan
metode:
v()

Constructor, zero-length vector.

v(Int n)

Constructor, vector of length n.

v(Int n, const T &a)

Constructor, initialize all elements to the

value a.

v(Int n, const T *a)

Constructor, initialize elements to values in a

C-style array,

a[0], a[1], : : :
v(const NRvector &rhs)

Copy constructor.

v.size()

Returns number of elements in v.

v.resize(Int newn)

Resizes v to size newn. We do not assume

that contents

are preserved.
v.assign(Int newn, const T &a) Resize v to size newn, and set all elements to

the value a.

v[Int i]

Element of v by subscript, either an l-value

and an r-value.

v = rhs

Assignment operator. Resizes v if necessary

and makes it a

Makes T available externally (useful in tem-

plated

copy of the vector rhs.


typedef T value_type;
functions or classes).
Seperti yang akan kita bahas nanti secara lebih rinci, Anda dapat menggunakan kelas
vektor Anda suka dengan Numerical Recipes, asalkan menyediakan fungsi dasar di atas.
Misalnya, cara kekerasan untuk menggunakan C ++ Standar Library vector <> kelas bukannya
NRvector adalah dengan direktif preprocessor
#define NRvector vector

(Bahkan, ada kompilator switch,_USESTDVECTOR_, dalam file nr3.h yang akan melakukan
hal ini.)
Metode untuk matriks yang erat analog. Jika vv adalah matriks tipe NRmatrix maka kita
asumsikan metode:
vv()

Constructor, zero-length vector.

vv(Int n, Int m)

Constructor, n _ m matrix.

vv(Int n, Int m, const T &a)

Constructor, initialize all elements to the

vv(Int n, Int m, const T *a)

Constructor, initialize elements by rows to the values in a C-

value a.

style array.
vv(const NRmatrix &rhs)

Copy constructor.

vv.nrows()

Returns number of rows n.

vv.ncols()

Returns number of columns m.

vv.resize(Int newn, Int newm) Resizes vv to newn_ newm. We do not assume that contents
are preserved.
vv.assign(Int newn, Int newm, Resizes vv to newn _ newm,
const t &a)

and sets all elements to the value a.

vv[Int i]

Return a pointer to the first element in row I

(not often used

v[Int i][Int j]

Element of vv by subscript, either an l-value

and an r-value.

vv = rhs

Assignment operator. Resizes vv if necessary

and makes it a

by itself).

copy of the matrix rhs.


typedef T value_type;

Makes T available externally

Untuk spesifikasi yang lebih tepat, lihat 1.4.3.


Ada satu properti tambahan yang kita asumsikan dari vektor dan matriks kelas, yaitu
bahwa semua elemen obyek disimpan secara berurutan. Untuk vektor, ini berarti bahwa unsurunsurnya dapat diatasi dengan pointer aritmetika relatif terhadap elemen pertama. Sebagai
contoh, jika kita memiliki
Vecdoub A(100);
Doub * b= & a[0];

maka [i] Dan B [i] referensi unsur yang sama, baik sebagai nilai 1 dan sebagai nilai r.
Kemampuan ini kadang-kadang penting untuk efisiensi dalam loop, dan hal ini juga berguna
untuk berinteraksi dengan kode warisan yang dapat menangani dua array *, tapi tidak vektor
VecDoub. Meskipun asli C ++ Standar tidak menjamin perilaku ini, semua implementasi dikenal
itu melakukannya, dan perilaku yang sekarang dibutuhkan oleh amandemen standar [2].
Untuk matriks, kita asumsikan analog adalah dengan baris dalam blok berurutan tunggal
sehingga, misalnya,
Int N=97, M=103;
Matdoub A(N,M);
Doub * b= & a[0][0];
menyiratkan bahwa [i] [j] dan b [m * i * j] adalah sama.
Beberapa dari rutinitas kita membutuhkan kemampuan mengambil sebagai argumen baik
vektor atau satu baris dari matriks. Untuk mempermudah, kita biasanya kode ini menggunakan
overloading, sebagai, misalnya,
void someroutine (Doub *v, Int m)
...
inline void someroutine (VecDoub &v)

{Version for a matrix row.}


{Version for a vectors.}

Untuk vektor v, panggilan terlihat seperti beberapa rutin (v), sedangkan untuk baris saya dari ay
matriks itu adalah beberapa rutin (& vv [i] [0], vv. ncols ()). Sementara sederhana argumen vv [i]
akan pada kenyataannya pekerjaan dalam pelaksanaan kami NRmatrix, mungkin tidak bekerja di
beberapa kelas matriks lain yang menjamin penyimpanan berurutan tetapi memiliki tipe kembali
untuk satu subscript berbeda dari T *.
1.4.3 Implementasi di nr3.h
Untuk referensi, di sini adalah deklarasi lengkap NRvector.
template <class T>
class NRvector {
private:
Size of array, indices 0..nnint nn;
T *v;

1.
Pointer to data array.

public:
NRvector();
explicit NRvector(int n);

Default constructor.
Construct vector of size n.
Initialize to constant value

NRvector(int n, const T &a);

a.
Initialize to values in C-

NRvector(int n, const T *a);


NRvector(const NRvector &rhs);
NRvector & operator=(const NRvector

style array a.
Copy constructor.

&rhs);
typedef T value_type;
inline T & operator[](const int i);
inline const T & operator[](const int i) const;
inline int size() const;
void resize(int newn);

Assignment operator.
Make T available.
Return element number i.
const version.
Return size of vector.
Resize, losing contents.
Resize and assign a to every

void assign(int newn, const T &a);

element.
Destructor

~NRvector();
.
Implementasi yang sederhana dan dapat ditemukan dalam file nr3 H. Satu-satunya masalah yang
membutuhkan kemahiran adalah pengobatan konsisten vektor nol-panjang dan menghindari
operasi resize yang tidak perlu.
Pernyataan lengkap NRmatrix adalah
template <class T>
class NRmatrix {
private:
Number of rows and
int nn;
int mm;

columns. Index
range is 0..nn-1, 0..mm1.

T **v;
public:

Storage for data.

NRmatrix();

Default constructor.

NRmatrix(int n, int m);

Construct n _ m matrix.
Initialize to constant

NRmatrix(int n, int m, const T &a);


NRmatrix(int n, int m, const T *a);

value a.
Initialize to values in C-

style array a.
NRmatrix(const NRmatrix &rhs);
NRmatrix & operator=(const

Copy constructor.

NRmatrix &rhs);

Assignment operator.

typedef T value_type;

Make T available.
Subscripting: pointer to

inline T* operator[](const int i);


inline const T* operator[](const int i)

row i.

const;

const version.

inline int nrows() const;

Return number of rows.


Return number of

inline int ncols() const;

columns.

void resize(int newn, int newm);


Resize, losing contents.
void assign(int newn, int newm, const Resize and assign a to
T &a);

every element.

~NRmatrix();

Destructor.

};
Beberapa rincian pelaksanaan di NRmatrix layak mengomentari. Variabel swasta ** poin
v tidak data melainkan untuk sebuah array dari pointer ke baris data. Alokasi memori dari array
ini terpisah dari alokasi ruang untuk data aktual. Ruang data dialokasikan sebagai blok tunggal,
tidak terpisah kemungkinan bahwa ada nol baris, atau bahwa ada sejumlah terbatas baris, tetapi
masing-masing dengan nol kolom. Jadi, misalnya, salah satu konstruktor seperti ini:
template <class T>
NRmatrix<T> : : NRmatrix (int n, int m) : nn(m) , v(n>0 ? new T*[n] : NULL)
{
int i, nel=m*n;
if (v) v[0] = nel>0 ? new T[nel] : NULL;
}
Akhirnya, itu penting sebuah lat apakah compiler Anda menghormati arahan inline di
NRvector dan NR matriks di atas. Jika tidak, maka Anda mungkin akan melakukan pemanggilan
fungsi penuh, menyimpan dan memulihkan konteks di prosesor, setiap kali Anda addres vektor
atau matriks elemen. Ini sama saja dengan membuat C ++ berguna untuk komputasi numerik
yang paling! Untungnya, seperti yang kita menulis, kompiler yang paling umum digunakan
adalah semua "terhormat" dalam hal ini.

1.5 Beberapa Konvensi lanjut dan Kemampuan


Kami kumpulkan di bagian ini beberapa penjelasan lebih lanjut dari kemampuan bahasa C +
+ dan bagaimana kita menggunakannya dalam buku ini.
1.5 .1 Kesalahan dan Exception Handling
Kami telah disebutkan bahwa kondisi kode kita error dengan pernyataan throw sederhana,
seperti ini
Throw (error foo in routine bah) ;
Jika Anda pemrograman di lingkungan yang memiliki seperangkat didefinisikan kelas kesalahan,
dan Anda ingin menggunakannya, maka Anda harus mengubah baris ini dalam rutinitas yang
Anda gunakan. Atau, tanpa tambahan mesin, Anda dapat memilih antara beberapa, perilaku
manfaat

yang

berbeda

hanya

dengan

membuat

perubahan

kecil

dalam

nr3

H.

Secara default, nr3 H mengubah lemparan () oleh makro preprocessor,


#define throw (message) \
{printf (ERROR: %\n in file %s at line %d\n, \
Message,--FILE-- , --LINE--); \
Exit (1) ; }
Ini menggunakan standar fitur ANSI C, juga hadir dalam C ++, untuk mencetak nama file
kode sumber dan nomor baris di mana kesalahan terjadi. Ini adalah janggal, tetapi sangat
fungsional.
Agak lebih fungsional, dan pasti lebih elegan, adalah untuk mengatur compiler
switch_USENRERRORCLASS_ nr3 h, yang bukan menggantikan kode berikut:
struct NRerror {
char *message;
char *file;
int line;
NRerror (char *m, char *f, int l) : message (m), file (f), line ( l ) {}
};
void NRcatch (NRerror err) {
printf (ERROR: %s\n in file %s at line %d\n,
err. message, err.file, err.line;
exit ( 1 );

};
#define throw (message) throw (NRerror (message,--FILE--,--LINE--));
Sekarang Anda memiliki (dasar) kelas kesalahan, NRerror, tersedia. Anda menggunakannya
dengan meletakkan mencobanya. . . struktur pengendalian cath pada setiap titik yang diinginkan
(atau poin) dalam kode Anda, misalnya (2,9),
try { Cholesky achola (a);}
catch (NRerror err) {NRcatch (err);

Executed if Cholesky throws an exception.}

Seperti ditunjukkan, penggunaan fungsi NRcatch atas hanya meniru perilaku makro sebelumnya
dalam konteks global. Tapi Anda tidak harus menggunakan NRcatch sama sekali:
1.5.2 Kebenaran konstanta
Beberapa topik dalam diskusi tentang C++ membangkitkan lebih banyak panas yang
mempertanyakan kata kunci tentang konstanta. Kami percaya perusahaan dalam menggunakan
konstanta sedapat mungkin, untuk mencapai apa yang disebut "kebenaran konstanta." Banyak
kesalahan coding secara otomatis terperangkap oleh compiler jika Anda memiliki kualifikasi
pengenal yang seharusnya tidak berubah dengan konstanta ketika mereka dinyatakan. Juga,
menggunakan konstanta membuat kode Anda jauh lebih mudah dibaca: ketika Anda melihat
konstanta di depan argumen ke fungsi, Anda segera tahu bahwa fungsi tidak akan memodifikasi
objek. Sebaliknya, jika konstanta tidak ada, Anda harus dapat konstanta pada objek yang berubah
di suatu tempat.
Kami adalah orang-orang const yang kuat seperti itu bahwa kita menyisipkan const
meskipun hal ini secara teoritis berlebihan: Jika argumen ini dilalui oleh nilai ke fungsi, maka
fungsi membuat salinan itu. Bahkan jika salinan ini dimodifikasi oleh fungsi, nilai asli tidak
berubah setelah keluar fungsi. Sementara ini memungkinkan Anda untuk mengubah, dengan
impunitas, nilai-nilai argumen yang telah melewati nilai, penggunaan ini rawan kesalahan dan
sulit untuk dibaca. Jika tujuan Anda dalam melewati sesuatu dengan nilai adalah bahwa itu
adalah variabel input saja, kemudian membuatnya jelas. Jadi kita mendeklarasikan fungsi f (x),
misalnya,

Jika dalam fungsi yang Anda ingin menggunakan variabel lokal yang initializedto x tapi
kemudian mendapat berubah, menentukan jumlah baru-tidak menggunakan x. Jika Anda
meletakkan konstanta dalam deklarasi, kompilator tidak akan membiarkan Anda salah.

Menggunakan konstanta dalam argumen fungsi Anda membuat fungsi Anda lebih umum:
Memanggil fungsi yang mengharapkan argumen konstanta dengan non - variabel konstanta
melibatkan "sepele" konversi. Tetapi berusaha untuk lulus kuantitas konstanta untuk non argumen konstanta adalah kesalahan.
Alasan terakhir untuk menggunakan konstanta adalah bahwa hal itu memungkinkan
pengguna tertentu - didefinisikan konversi yang akan dibuat. Sebagaimana dijelaskan dalam [1],
ini dapat berguna jika Anda ingin menggunakan Numerical Recipes rutinitas dengan yang lain
matriks / vektor kelas perpustakaan.
Kita sekarang perlu untuk menguraikan apa sebenarnya konstanta tidak untuk tipe non
sederhana seperti sebuah kelas yang merupakan argumen dari fungsi. Pada dasarnya, itu
menjamin bahwa objek tersebut tidak diubah oleh fungsi. Dengan kata lain, anggota objek data
tidak berubah. Tetapi jika anggota saat ini adalah pointer ke data yang sama, dan data itu sendiri
bukanlah variabel anggota, maka data dapat berubah meskipun pointer tidak bisa.
Mari kita lihat implikasi dari ini untuk f fungsi yang mengambil N vektor <diragukan>
argumen. untuk menghindari menyalin yang tidak perlu, kami selalu melewati vektor dan
matriks dengan referensi. Mempertimbangkan perbedaan antara menyatakan argumen dari suatu
fungsi dengan dan tanpa konstanta:

Versi konstanta menjanjikan bahwa f tidak memodifikasi data anggota dari. Tapi pernyataan
seperti

Dalam definisi fungsi pada prinsipnya sangat OK - Anda memodifikasi data menunjuk, bukan
pointer itu sendiri.
"Apakah tidak ada cara untuk melindungi data?" Anda mungkin bertanya. Ya, ada: kamu
dapat mendeklarasikan jenis kembalinya operator subscript, operator [], menjadi konstanta. ini
adalah mengapa ada dua versi operator [] di kelas vektor,

Bentuk pertama mengembalikan referensi ke elemen vektor dimodifikasi, sedangkan


pengembalian kedua unmodifiable elemen vektor (karena tipe kembali memiliki konstanta di
depan).
Tapi bagaimana compiler tahu versi yang mana untuk memanggil ketika Anda hanya
menulis [1]? Yang ditentukan oleh konstanta kata membuntuti di versi kedua. Hal ini mengacu
tidak elemen kembali, atau untuk argumen saya, tapi untuk objek yang operator [] sedang
dipanggil, IS contoh kita vektor a. Secara bersama-sama, dua versi mengatakan ini ke compiler:
"Jika vektor adalah konstanta, transfer yang const'knees ke elemen am kembali [i]. Jika tidak,
maka tidak.
Pertanyaan yang tersisa adalah ini bagaimana compiler menentukan apakah suatu
konstanta. Dalam contoh kita, di mana adalah argumen fungsi, itu adalah sepele: argumen yang
baik dinyatakan sebagai konstanta atau tidak. Dalam konteks lain, mungkin konstanta karena
Anda awalnya menyatakan itu adalah anggota data referensi konstanta di beberapa objek lain,
atau untuk beberapa lainnya, yang lebih misterius, alasannya.
Seperti yang Anda lihat, Gatting konstanta untuk melindungi data adalah sedikit rumit.
Dilihat dari sejumlah besar perpustakaan matriks / vektor yang mengikuti skema ini, banyak
orang merasa bahwa hasil yang berharga. Kami mendorong Anda selalu menyatakan sebagai
konstanta mereka objek dan variabel yang tidak dimaksudkan untuk dimodifikasi. Anda
melakukan ini baik pada saat sebuah objek sebenarnya dibuat dan dalam argumen dari deklarasi
fungsi dan definisi. Anda tidak akan menyesal membuat kebiasaan kebenaran konstanta.
Di $ 1,4 kita mengartikan vektor sebuah nama jenis matriks dengan label Trailing _I,
misalnya, VecDoub_I and MatInt_I. The _I, yang merupakan singkatan dari "masukan ke
fungsi," berarti bahwa jenis dinyatakan sebagai konstanta. (Ini sudah dilakukan dalam laporan
typedef, Anda tidak perlu mengulanginya.) Label sesuai _0 dan _I0 yang mengingatkan Anda
ketika argumen tidak hanya non-konstanta, tapi benar-benar akan diubah oleh fungsi yang dalam
argumen daftar mereka muncul.

Setelah benar menempatkan semua penekanan pada kebenaran konstanta, tugas memaksa
kita juga mengakui keberadaan filsafat alternatif, yang adalah untuk tetap dengan tampilan lebih
sederhana "konstanta melindungi wadah, bukan isi." Dalam hal ini Anda akan ingin hanya salah
satu bentuk operator [], yaitu
T & operator [] (const int i) const;

Ini akan dipanggil apakah vektor Anda disahkan oleh referensi konstanta atau tidak. Dalam
kedua kasus elemen i dikembalikan sebagai berpotensi dimodifikasi. Sementara kita menentang
ini secara filosofis, ternyata itu memungkinkan semacam rumit dari jenis konversi otomatis yang
memungkinkan Anda untuk menggunakan vektor dan matriks kelas favorit Anda, bukan vektor
dan matriks, bahkan jika kelas Anda menggunakan sintaks yang sama sekali berbeda dari apa
yang telah kita diasumsikan. Untuk informasi tentang aplikasi yang sangat khusus ini, lihat [1].
1.5.3 Kelas Dasar Abstrak (ABC), atau Template
Kadang-kadang ada lebih dari satu cara yang baik untuk mencapai suatu tujuan di C ++.
Heck, mari kita jujur: Selalu ada lebih dari satu cara. Kadang-kadang perbedaan berjumlah tweak
kecil, tapi di lain waktu mereka mewujudkan pandangan yang sangat berbeda tentang bahasa.
Ketika kita membuat satu pilihan tersebut, dan kamu lebih memilih yang lain, Anda akan
menjadi sangat kesal dengan kami. Pertahanan kita terhadap hal ini untuk menghindari
konsistensi bodoh, * dan untuk menggambarkan banyak sudut pandang mungkin.
Sebuah contoh yang baik adalah pertanyaan tentang kapan harus menggunakan kelas
dasar abstrak (ABC) versus template, ketika kemampuan mereka tumpang tindih. Misalkan kita
memiliki fungsi func yang dapat melakukan nya (berguna) hal pada, atau menggunakan,
beberapa jenis objek, menyebut mereka ObjA, ObjB, dan ObjC. Selain itu, func tidak perlu tahu
banyak tentang obyek berinteraksi dengan, hanya bahwa ia memiliki beberapa metode Tellme.
Kita bisa menerapkan konfigurasi ini sebagai kelas dasar abstrak:
struct ObjABC {
Abstract Base Class for objects with
tellme.
virtual void tellme() = 0;
};
struct ObjA : ObjABC {
Derived class.
...
void tellme() {...}
};
struct ObjB : ObjABC {
Derived class.
...
void tellme() {...}
};
struct ObjC : ObjABC {
Derived class.
...
void tellme() {...}
};
void func(ObjABC &x) {
...

x.tellme();
}
Di sisi lain, dengan menggunakan template, kita dapat menulis kode untuk func tanpa
pernah melihat (atau bahkan mengetahui nama-nama) objek yang dimaksudkan:
template<class T>
void func(T &x) {
...
x.tellme();
}
Yang pasti tampaknya lebih mudah! Apakah lebih baik?
Mungkin. Kelemahan dari template adalah bahwa template harus tersedia untuk compiler
setiap kali bertemu panggilan untuk func. Hal ini karena sebenarnya mengkompilasi versi yang
berbeda dari func untuk setiap jenis yang berbeda dari argumen T yang bertemu. Kecuali kode
Anda begitu besar sehingga tidak dapat dengan mudah disusun sebagai satu kesatuan, namun, ini
tidak banyak kerugian. Di sisi lain, mendukung template, adalah kenyataan bahwa fungsi virtual
dikenakan hukuman run-time kecil ketika mereka dipanggil. Tapi ini jarang signifikan.
Faktor penentu dalam contoh ini berhubungan dengan rekayasa perangkat lunak, bukan kinerja,
dan tersembunyi di baris dengan elips (...). Kami belum benar-benar memberitahu Anda
bagaimana berhubungan erat ObjA, ObjB, dan ObjC yang. Jika mereka dekat, maka pendekatan
ABC menawarkan kemungkinan menempatkan lebih dari sekedar Tellme ke kelas dasar.
Menempatkan sesuatu ke dalam kelas dasar, apakah data atau metode virtual murni,
memungkinkan compiler menegakkan konsistensi di kelas turunan. Jika nanti menulis objek
ObjD lain berasal, konsistensi juga akan diberlakukan. Sebagai contoh, kompiler akan meminta
Anda untuk menerapkan metode dalam setiap kelas turunan sesuai dengan setiap metode virtual
murni di kelas dasar.
Sebaliknya, dalam pendekatan Template, hanya konsistensi ditegakkan akan bahwa metode
Tellme ada, dan ini hanya akan diberlakukan pada titik dalam kode di mana func sebenarnya
disebut dengan argumen ObjD (jika titik tersebut ada), tidak pada titik di mana ObjD
didefinisikan. Memeriksa pendekatan Template konsistensi demikian agak lebih serampangan.
Santai programmer akan memilih template. Up-ketat programmer akan memilih ABC.
Kami memilih untuk. . . kedua, pada kesempatan yang berbeda. Ada juga alasan lain bisa, yang
berkaitan dengan fitur halus kelas derivasi atau template, untuk memilih salah satu pendekatan di
atas yang lain. Kami akan menunjukkan ini sebagai kami bertemu dengan mereka di bab
berikutnya. Sebagai contoh, dalam Bab 17 kita mendefinisikan kelas dasar abstrak yang disebut
Stepper dasar untuk berbagai "stepper" rutinitas untuk memecahkan Odes. Kelas yang berasal
mengimplementasikan algoritma loncatan tertentu, dan mereka templated sehingga mereka bisa
menerima fungsi atau functor argumen (lihat $ 1.3.3).

1.5.4 NaN dan Mengambang Titik Pengecualian


Kita disebutkan di _1.1.1 bahwa standar IEEE floating-point termasuk representasi untuk
NaN, yang berarti "bukan angka." NaN berbeda dari infinity positif dan negatif, serta dari setiap
nomor representable. Hal ini dapat menjadi berkat dan sebuah kutukan.
Berkat adalah bahwa hal itu dapat berguna untuk memiliki nilai yang dapat digunakan
dengan arti seperti "tidak memproses me" atau "data yang hilang" atau "belum diinisialisasi."
Untuk menggunakan NaN dalam mode ini, kamu harus mampu untuk mengatur variabel untuk
itu, dan kamu harus mampu untuk menguji nya yang telah ditetapkan.
Pengaturan mudah. The "disetujui" metode adalah dengan menggunakan numeric_limits.
Dalam nr3.h garis
static const Doub NaN = numeric_limits<Doub>::quiet_NaN();
mendefinisikan nilai global NaN, sehingga Anda dapat menulis hal-hal seperti
x = NaN;
sesuka hati. Jika Anda pernah menemukan sebuah compiler yang tidak melakukan hak ini (itu
sudut yang cukup jelas dari perpustakaan standar!), Maka baik cobalah.
Uint proto_nan[2]=0xffffffff, 0x7fffffff;
double NaN = *( double* )proto_nan;

(yang mengasumsikan perilaku little-endian; lih _1.1.1) atau diri penjelas


Doub NaN = sqrt(-1.);
yang mungkin, namun, melemparkan pengecualian segera (lihat di bawah) dan dengan demikian
tidak bekerja untuk tujuan ini. Tapi, satu atau lain cara, kamu dapat umumnya mencari cara
untuk mendapatkan konstan NaN ke lingkungan Anda.
Testing also requires a bit of (one-time) experimentation: According to the IEEE
standard, NaN is guaranteed to be the only value that is not equal to itself
Jadi, metode "disetujui" pengujian apakah nilai Doub x telah diatur untuk NaN adalah
if (x! = x) {...} Ini adalah NaN!

(atau tes untuk kesetaraan untuk menentukan bahwa itu bukan NaN). Sayangnya, pada saat
penulisan, beberapa kompiler dinyatakan sempurna baik tidak melakukannya dengan benar.
Sebaliknya, mereka menyediakan Isnan makro () yang mengembalikan true jika argumen adalah
NaN, jika tidak palsu. (Periksa dengan teliti apakah #include diperlukan adalah math.h atau
float.h-itu bervariasi.)
Apa, kemudian, adalah kutukan NaN? Itu adalah bahwa beberapa compiler, terutama
Microsoft, telah kurang pikir-out perilaku default di membedakan tenang NaN dari sinyal NaN.
Kedua jenis NaN didefinisikan dalam standar IEEE floating-point. NaN tenang seharusnya untuk
keperluan seperti yang di atas: Anda dapat mengatur mereka, menguji mereka, dan menyebarkan
mereka dengan tugas, atau bahkan melalui operasi floating lainnya. Dalam penggunaan seperti
mereka tidak seharusnya sinyal pengecualian yang menyebabkan program Anda untuk
membatalkan. Signalling NaN, di sisi lain, yang, seperti namanya, seharusnya sinyal
pengecualian. Signalling NaN harus dihasilkan oleh operasi yang tidak valid, seperti akar kuadrat
atau logaritma dari angka negatif, atau pow (0, 0.).
Jika semua NaN diperlakukan sebagai sinyal pengecualian, maka Anda tidak dapat
menggunakan mereka seperti yang kita telah disarankan di atas. Itu mengganggu, tapi OK. Di
sisi lain, jika semua NaN diperlakukan sebagai tenang (default Microsoft pada saat menulis),
maka Anda akan menjalankan perhitungan panjang hanya untuk menemukan bahwa semua hasil
yang NaN-dan Anda tidak memiliki cara untuk mencari operasi tidak valid yang memicu
kaskade menyebarkan dari (tenang) NaN. Itu tidak OK. Itu membuat debugging mimpi buruk.
(Anda bisa mendapatkan penyakit yang sama jika pengecualian floating-point lainnya
menyebarkan, misalnya overflow atau divisi-oleh-nol.) Trik untuk compiler tertentu tidak berada
dalam lingkup normal kita. Tapi yang satu ini begitu penting bahwa kita membuat sebuah
"pengecualian": Jika Anda tinggal di planet Microsoft, maka baris kode,
int cw = _controlfp(0,0);
cw &=~(EM_INVALID | EM_OVERFLOW | EM_ZERODIVIDE );
_controlfp(cw,MCW_EM);
pada awal program Anda akan berubah NaN dari operasi yang tidak valid, meluap, dan
membagi-by-nol menjadi sinyal NaN, dan meninggalkan semua tenang NaN lainnya. Ada saklar
compiler, _TURNONFPES_ di nr3.h yang akan melakukan ini untuk Anda secara otomatis.
(Pilihan selanjutnya adalah EM_UNDERFLOW, EM_INEXACT, dan EM_DENORMAL, tapi
kami pikir ini yang terbaik diserahkan tenang.)\
1.5.5 VARIA
Bounds memeriksa vektor dan matriks, yaitu, memverifikasi bahwa subskrip berada
dalam jangkauan, mahal. Hal ini dapat dengan mudah dua atau tiga waktu akses ke
subscript elemen. Dalam konfigurasi default mereka, NRvector dan NRmatrix kelas

tidak pernah melakukan pengecekan batas. Namun, nr3.h memiliki saklar compiler,
_CHECKBOUNDS_, yang ternyata batas memeriksa. Ini dilaksanakan oleh
preprocessor arahan untuk kompilasi bersyarat sehingga tidak ada hukuman kinerja
ketika
Anda
meninggalkan
dimatikan.
Ini
jelek,
tapi
efektif.
Vektor <> kelas dalam ++ Standard Library C membutuhkan taktik yang berbeda. Jika
Anda mengakses elemen vektor dengan sintaks v [i], tidak ada batas memeriksa. Jika
Anda bukan menggunakan metode di (), sebagai v.at (i), maka batas memeriksa
dilakukan. Kelemahan yang jelas dari pendekatan ini adalah bahwa Anda tidak dapat
dengan mudah mengubah program yang panjang dari satu metode untuk yang lain,
seperti yang Anda mungkin ingin lakukan ketika debugging.
Pentingnya kinerja menghindari menyalin yang tidak perlu dari benda-benda besar,
seperti vektor dan matriks, tidak bisa terlalu ditekankan. Seperti telah disebutkan,
mereka harus selalu dikirimkan dengan referensi dalam fungsi argumen. Tapi Anda
juga perlu berhati-hati tentang, atau menghindari sepenuhnya, penggunaan fungsi yang
jenis kembali adalah objek besar. Hal ini berlaku bahkan jika jenis kembali adalah
referensi (yang merupakan bisnis yang rumit pula). Pengalaman kami adalah bahwa
kompiler sering membuat objek sementara, menggunakan copy constructor, ketika
kebutuhan untuk melakukannya tidak jelas atau tidak ada. Itulah sebabnya kami begitu
sering menulis kekosongan fungsi yang memiliki argumen tipe (misalnya)
MatDoub_O untuk mengembalikan "jawaban." (Ketika kita menggunakan vektor atau
matriks kembali jenis, alasan kami adalah baik bahwa kode ini pedagogis, atau bahwa
overhead diabaikan dibandingkan dengan beberapa perhitungan besar yang baru saja
dilakukan) Anda dapat memeriksa compiler Anda dengan instrumenting vektor dan
matriks kelas:. Menambahkan variabel integer statis untuk definisi kelas, kenaikan itu
dalam copy constructor dan operator penugasan, dan melihat nilai sebelum dan
sesudah operasi yang (menurut Anda) tidak harus memerlukan salinan. Anda mungkin
akan terkejut.
Hanya ada dua rutinitas di Numerical Recipes yang menggunakan array tiga dimensi,
rlft3 di _12.6, dan solvde di _18.3. File nr3.h termasuk kelas dasar untuk array tiga
dimensi, terutama untuk layanan dua rutinitas ini. Dalam banyak aplikasi, cara yang
lebih baik untuk melanjutkan adalah untuk menyatakan vektor matriks, misalnya,
vector<MatDoub> threedee(17);
for (Int i=0;i<17;i++) threedee[i].resize(19,21);
yang menciptakan, pada dasarnya, array tiga dimensi ukuran 17 _ 19 _ 21. Anda
dapat mengatasi komponen individu sebagai threedee [i] [j] [k].

Di masa lalu, Numerical Recipes termasuk ketentuan untuk unit- atau satu
berdasarkan, bukan berbasis nol, indeks array. Versi tersebut terakhir diterbitkan pada
tahun 1992. array Nol berbasis telah menjadi begitu diterima secara universal bahwa
kita tidak lagi mendukung pilihan lain.

Anda mungkin juga menyukai