Jawaban singkat:
Spesialisasi pow(x, n)ke mana nbilangan asli sering berguna untuk kinerja waktu . Tetapi generik pustaka standar pow()masih berfungsi dengan baik ( mengejutkan! ) Untuk tujuan ini dan sangat penting untuk menyertakan sesedikit mungkin dalam pustaka C standar sehingga dapat dibuat portabel dan semudah mungkin diimplementasikan. Di sisi lain, itu tidak menghentikannya sama sekali dari berada di pustaka standar C ++ atau STL, yang saya cukup yakin tidak ada yang berencana menggunakannya dalam beberapa jenis platform tertanam.
Sekarang, untuk jawaban panjangnya.
pow(x, n)dalam banyak kasus dapat dibuat lebih cepat dengan mengkhususkan diri npada bilangan asli. Saya harus menggunakan implementasi saya sendiri dari fungsi ini untuk hampir setiap program yang saya tulis (tetapi saya menulis banyak program matematika dalam C). Operasi khusus dapat dilakukan O(log(n))tepat waktu, tetapi bila nkecil, versi linier yang lebih sederhana dapat lebih cepat. Berikut implementasi keduanya:
// Computes x^n, where n is a natural number.
double pown(double x, unsigned n)
{
double y = 1;
// n = 2*d + r. x^n = (x^2)^d * x^r.
unsigned d = n >> 1;
unsigned r = n & 1;
double x_2_d = d == 0? 1 : pown(x*x, d);
double x_r = r == 0? 1 : x;
return x_2_d*x_r;
}
// The linear implementation.
double pown_l(double x, unsigned n)
{
double y = 1;
for (unsigned i = 0; i < n; i++)
y *= x;
return y;
}
(Saya pergi xdan nilai kembaliannya berlipat ganda karena hasil dari pow(double x, unsigned n)akan masuk ganda sesering pow(double, double)mungkin.)
(Ya, pownitu rekursif, tetapi memecahkan tumpukan sama sekali tidak mungkin karena ukuran tumpukan maksimum kira-kira akan sama log_2(n)dan nmerupakan bilangan bulat. Jika nadalah bilangan bulat 64-bit, itu memberi Anda ukuran tumpukan maksimum sekitar 64. Tidak ada perangkat keras yang sedemikian ekstrim keterbatasan memori, kecuali untuk beberapa PIC cerdik dengan tumpukan perangkat keras yang hanya melakukan panggilan fungsi 3 hingga 8 dalam.)
Mengenai kinerja, Anda akan terkejut dengan kemampuan berbagai taman pow(double, double). Saya menguji seratus juta iterasi pada IBM Thinkpad saya yang berusia 5 tahun dengan xangka iterasi yang nsama dan sama dengan 10. Dalam skenario ini, pown_lmenang. glibc pow()membutuhkan 12,0 detik pengguna, pown7,4 detik pengguna, dan pown_lhanya 6,5 detik pengguna. Jadi itu tidak terlalu mengejutkan. Kami kurang lebih mengharapkan ini.
Kemudian, saya biarkan xkonstan (saya setel menjadi 2,5), dan saya mengulang ndari 0 hingga 19 seratus juta kali. Kali ini, secara tidak terduga, glibc powmenang, dan dengan telak! Hanya butuh 2.0 detik pengguna. Saya pownmengambil 9,6 detik, dan pown_lmengambil 12,2 detik. Apa yang terjadi disini? Saya melakukan tes lain untuk mencari tahu.
Saya melakukan hal yang sama seperti di atas hanya dengan xsetara dengan satu juta. Kali ini, pownmenang di 9,6s. pown_lmengambil 12.2s dan glibc pow mengambil 16.3s. Sekarang, sudah jelas! glibc powberkinerja lebih baik daripada ketiganya saat xrendah, tetapi terburuk saat xtinggi. Saat xtinggi, pown_lberkinerja terbaik saat nrendah, dan pownberkinerja terbaik saat xtinggi.
Jadi, inilah tiga algoritme berbeda, masing-masing mampu berkinerja lebih baik daripada yang lain dalam situasi yang tepat. Jadi, pada akhirnya, mana yang akan digunakan kemungkinan besar tergantung pada bagaimana Anda berencana menggunakan pow, tetapi menggunakan versi yang tepat itu sepadan, dan memiliki semua versi itu bagus. Bahkan, Anda bahkan dapat mengotomatiskan pilihan algoritme dengan fungsi seperti ini:
double pown_auto(double x, unsigned n, double x_expected, unsigned n_expected) {
if (x_expected < x_threshold)
return pow(x, n);
if (n_expected < n_threshold)
return pown_l(x, n);
return pown(x, n);
}
Selama x_expecteddan n_expectedmerupakan konstanta yang diputuskan pada waktu kompilasi, bersama dengan kemungkinan beberapa peringatan lainnya, kompiler pengoptimalan yang bernilai garamnya akan secara otomatis menghapus seluruh pown_autopemanggilan fungsi dan menggantinya dengan pilihan yang sesuai dari ketiga algoritme. (Sekarang, jika Anda benar-benar akan mencoba menggunakan ini, Anda mungkin harus sedikit bermain-main dengannya, karena saya tidak benar-benar mencoba menyusun apa yang saya tulis di atas.;))
Di sisi lain, glibc pow berfungsi dan glibc sudah cukup besar. Standar C seharusnya portabel, termasuk ke berbagai perangkat yang disematkan (pada kenyataannya pengembang yang disematkan di mana-mana umumnya setuju bahwa glibc sudah terlalu besar untuk mereka), dan itu tidak bisa portabel jika untuk setiap fungsi matematika sederhana perlu menyertakan setiap algoritma alternatif yang mungkin bisa digunakan. Jadi, itulah mengapa tidak ada dalam standar C.
catatan kaki: Dalam pengujian kinerja waktu, saya memberikan fungsi saya tanda pengoptimalan yang relatif murah hati ( -s -O2) yang cenderung sebanding dengan, jika tidak lebih buruk dari, apa yang mungkin digunakan untuk mengkompilasi glibc di sistem saya (archlinux), jadi hasilnya mungkin adil. Untuk tes yang lebih ketat, aku harus mengkompilasi glibc diri saya dan saya reeeally tidak merasa seperti melakukan hal itu. Saya dulu menggunakan Gentoo, jadi saya ingat berapa lama waktu yang dibutuhkan, bahkan ketika tugasnya otomatis . Hasilnya cukup meyakinkan (atau agak tidak meyakinkan) bagi saya. Anda tentu saja dipersilakan untuk melakukannya sendiri.
Putaran bonus: Spesialisasi dari pow(x, n)semua bilangan bulat sangat penting jika diperlukan keluaran bilangan bulat yang tepat, yang memang terjadi. Pertimbangkan mengalokasikan memori untuk larik berdimensi-N dengan elemen p ^ N. Mendapatkan p ^ N off bahkan satu akan menghasilkan kemungkinan segfault yang terjadi secara acak.