Data sudah tersimpan rapi — vektor, matriks, dan bilangan complex dari chapter-chapter sebelumnya kini tinggal dihitung, dan di sinilah <cmath> masuk: satu header berisi seluruh fungsi matematika standar C++ mulai dari akar kuadrat, eksponensial, logaritma, sampai trigonometri. Header ini juga tempat tinggal aturan-aturan halus yang sering menggigit: sqrt dari bilangan negatif tidak melempar exception melainkan mengembalikan NaN yang diam-diam menular ke hasil akhir, fmod dan remainder memberi sisa pembagian yang berbeda arah untuk input negatif, dan static_cast<int> memotong ke arah nol alih-alih membulatkan. Chapter ini menurunkan semuanya ke satu program teruji, lalu menutup dengan resep konversi antar tipe angka yang memeriksa rentang sebelum mempersempit — bukan sesudah hasilnya tak masuk akal.
Peta Fungsi <cmath> #
| Kelompok | Fungsi | Catatan pemakaian |
|---|---|---|
| Akar & pangkat | sqrt, pow, cbrt | sqrt(x) untuk x negatif → NaN; pow(2,10) = 1024 |
| Eksponen & log | exp, log (basis e), log10, log2 | log bilangan nonpositif → NaN atau -inf pada nol |
| Trigonometri | sin, cos, tan, plus asin, atan2 | argumen dalam radian, bukan derajat |
| Nilai mutlak | fabs (double), abs (integer, dari <cstdlib>) | satu nama, dua dunia tipe |
| Sisa pembagian | fmod, remainder | berbeda arah pemotongan untuk input negatif |
| Pembulatan | floor, ceil, trunc, round | trunc memotong ke nol; round ke terdekat (0,5 ke atas) |
Semua fungsi utama bekerja di atas double; ada overload untuk float dan long double, serta varian s dan f (sinf, sqrtl) untuk pemanggilan eksplisit. Pemisahan abs/fabs adalah warisan C yang penting dihafal: memanggil std::abs pada double sebenarnya berfungsi karena overload modern, tetapi menyebut fabs membuat maksudnya tak bisa salah dibaca.
Program Teruji #
#include <cmath>
#include <iostream>
#include <limits>
int main() {
// 1. Fungsi dasar dari <cmath>
std::cout << "sqrt(2) = " << std::sqrt(2.0) << "\n";
std::cout << "pow(2,10) = " << std::pow(2.0, 10.0) << "\n";
std::cout << "exp(1) = " << std::exp(1.0) << "\n";
std::cout << "log(100) = " << std::log(100.0) << "\n";
std::cout << "log10(100) = " << std::log10(100.0) << "\n";
std::cout << "sin(pi/6) = "
<< std::sin(std::acos(-1.0) / 6.0) << "\n";
// 2. abs untuk integer, fabs untuk floating-point
std::cout << "fabs(-2.5) = " << std::fabs(-2.5) << "\n";
std::cout << "abs(-7) = " << std::abs(-7) << "\n";
// 3. fmod vs remainder: dua arah "sisa pembagian" floating
std::cout << "fmod(10,3.5) = " << std::fmod(10.0, 3.5) << "\n";
std::cout << "remainder(10,3.5) = " << std::remainder(10.0, 3.5) << "\n";
std::cout << "fmod(-10,3.5) = " << std::fmod(-10.0, 3.5) << "\n";
std::cout << "remainder(-10,3.5)= " << std::remainder(-10.0, 3.5) << "\n";
// 4. Nilai khusus: sqrt negatif menghasilkan NaN, deteksi dengan isnan
double akarNegatif = std::sqrt(-1.0);
std::cout << "sqrt(-1) = " << akarNegatif << "\n";
std::cout << "isnan(akar) = "
<< std::boolalpha << std::isnan(akarNegatif) << "\n";
// 5. Konversi: truncation vs pembulatan eksplisit
double harga = 3.99;
std::cout << "static_cast<int>(3.99) = "
<< static_cast<int>(harga) << "\n";
std::cout << "static_cast<int>(-3.99) = "
<< static_cast<int>(-harga) << "\n";
std::cout << "round(3.5) = " << std::round(3.5) << "\n";
std::cout << "floor(-3.9) = " << std::floor(-3.9) << "\n";
std::cout << "ceil(3.1) = " << std::ceil(3.1) << "\n";
// 6. Cast aman: periksa rentang sebelum mempersempit tipe
double besar = 3e9;
bool aman = besar >= std::numeric_limits<int>::lowest()
&& besar <= std::numeric_limits<int>::max();
std::cout << "3e9 muat di int? = " << aman << "\n";
return 0;
}Output yang Dihasilkan:
sqrt(2) = 1.41421
pow(2,10) = 1024
exp(1) = 2.71828
log(100) = 4.60517
log10(100) = 2
sin(pi/6) = 0.5
fabs(-2.5) = 2.5
abs(-7) = 7
fmod(10,3.5) = 3
remainder(10,3.5) = -0.5
fmod(-10,3.5) = -3
remainder(-10,3.5)= 0.5
sqrt(-1) = -nan
isnan(akar) = true
static_cast<int>(3.99) = 3
static_cast<int>(-3.99) = -3
round(3.5) = 4
floor(-3.9) = -4
ceil(3.1) = 4
3e9 muat di int? = falseBedah baris-per-baris:
- Baris 7-13 (
sqrt ... sin): enam fungsi dasar dengan hasil yang sesuai matematika —exp(1)≈ 2,71828 (bilangan e),log(100)≈ 4,60517 (basis e), dansin(π/6)= 0,5. Sudut trigonometri dibangun daristd::acos(-1.0)sebagai nilai π; semua argumen dalam radian. - Baris 16-17 (
fabs(-2.5) ... abs(-7)): dua fungsi nilai mutlak untuk dua dunia tipe —fabsmenerimadouble,absmenerima integer. Hasilnya 2,5 dan 7 seperti diharapkan. - Baris 20-23 (
fmod ... remainder): perbedaan keduanya baru terlihat pada input negatif.fmod(10, 3.5)= 3 memotong hasil bagi ke arah nol (10/3,5 = 2,857 → 2, sisa 3);remainder(10, 3.5)= -0.5 membulatkan hasil bagi ke terdekat (3, sisa -0,5). Untuk input positif keduanya sering sama; untuk negatif,fmod(-10, 3.5)mempertahankan tanda pembilang (-3) sedangkanremainder(-10, 3.5)mengambil jalur terdekat (0,5). - Baris 26-29 (
std::sqrt(-1.0) ... std::isnan(...)): akar dari bilangan negatif di ranahdoublemenghasilkan NaN, tercetak-nan— tanda minus hiasan dari bit sign yang tidak bermakna, seperti dibedah di Chapter Floating Point Representation. Fungsistd::isnanadalah satu-satunya cara sah mendeteksinya, karena perbandingan biasa terhadap NaN selalufalse. - Baris 32-39 (
harga = 3.99 ... static_cast ... round ... floor ... ceil):static_cast<int>memotong ke arah nol: 3.99 → 3 dan -3.99 → -3 — bukan pembulatan, melainkan pembuangan desimal. Bila pembulatan yang dimaksud, panggil fungsi eksplisitnya:round(3.5)= 4,floor(-3.9)= -4 (ke bawah),ceil(3.1)= 4 (ke atas). - Baris 42-45 (
double besar = 3e9; bool aman = ...): 3 miliar berada di luar rentangint(maksimum 2.147.483.647), dan cek terhadaplowest()/max()mendeteksinya sebelum cast dijalankan. Tanpa cek ini, konversi double berukuran besar ke integer adalah undefined behavior — bukan sekadar angka aneh.
Konversi Aman dalam Praktik #
Pola baris 42-45 bisa Anda jadikan fungsi kecil yang dipakai berulang: periksa nilai terhadap std::numeric_limits<Target>::lowest() dan max(), kembalikan bool atau std::optional<Target> bila lolos. Dua aturan pendamping: bila tujuan Anda pembulatan, panggil std::round dulu baru cast (dan ingat hasilnya masih bisa meluap); dan bila input datang dari teks, pertimbangkan std::from_chars yang mengembalikan error terstruktur — dibahas pada Section Error Handling. Konversi melebar (int ke double, float ke double) justru selalu aman untuk rentangnya; yang perlu diwaspadai hanya kehilangan presisi ketika int 64-bit berisi nilai lebih dari 2⁵³ dimasukkan ke double.
Seluruh peralatan numerik kini sudah lengkap: representasi tipe, presisi, galat penjumlahan, pustaka matematika, bahkan bilangan acak. Chapter terakhir section ini merangkai semuanya menjadi disiplin kerja — cara menguji akurasi hasil, memilih metode yang stabil, dan keputusan-keputusan desain yang menjaga perhitungan tetap bisa dipercaya.
Rujukan resmi untuk topik Chapter ini: fungsi matematika umum <cmath>
dan std::isnan
di cppreference.