Menu

Fungsi matematika di C: math.h, sqrt, pow, dan linking dengan -lm

Tur singkat math.h: sqrt, pow, fabs, floor dan ceil, round, fmod, keluarga fungsi trigonometri dan logaritma, INFINITY dan NAN. Ditambah flag linker -lm yang jadi penyebab error klasik undefined reference.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Bahasa inti C memberimu + - * / dan %, dan tidak lebih: tidak ada operator pangkat, tidak ada akar kuadrat, tidak ada pembulatan. Semua yang lebih dari aritmetika ada di library matematika standar, yang dideklarasikan di <math.h>.

Semua fungsi ini bekerja dengan presisi double: menerima argumen double dan mengembalikan double. Itu hal pertama yang perlu kamu pahami betul, karena memberikan int tetap bekerja tanpa suara (nilainya dikonversi), sementara menyimpan hasilnya ke int diam-diam membuang pecahannya.

Memulai, dan flag -lm

Di komputermu sendiri, mengompilasi kode itu bisa gagal:

/usr/bin/ld: /tmp/ccXYZ.o: in function `main':
program.c:(.text+0x1a): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

Inilah sandungan paling umum saat memakai math.h, dan itu bukan kesalahan di kodemu. Header sudah menyediakan deklarasinya, jadi compiler puas; implementasinya ada di library terpisah (libm) yang secara bawaan tidak disertakan linker di Linux. Tambahkan -lm:

gcc program.c -o program -lm

Flag itu harus ditulis setelah file source: linker memproses input-nya secara berurutan, dan library yang ditulis sebelum kode yang membutuhkannya tidak menyelesaikan apa pun. Di macOS dan dengan MinGW di Windows, rutin matematika sudah menjadi bagian library C standar, jadi -lm tidak diperlukan (meski tidak berbahaya di macOS).

Pangkat dan akar

sqrt(x) untuk angka negatif mengembalikan NaN, bukan membuat program crash; jaga argumennya kalau nilainya bisa negatif. hypot layak diketahui karena menghitung nilai yang sama dengan sqrt(x*x + y*y) tanpa overflow saat x atau y sangat besar.

Untuk pangkat integer yang kecil, perkalian biasa lebih cepat sekaligus tepat: x * x lebih baik daripada pow(x, 2.0), yang lewat logaritma dan bisa mengembalikan 8.999999999999998 padahal kamu berharap 9.

Pembulatan: floor, ceil, round, trunc

Empat fungsi, empat jawaban berbeda untuk angka negatif. Tabel itulah bagian yang layak dihafal:

  • floor(x): integer terbesar yang tidak lebih besar dari x. Selalu bergerak ke arah minus tak hingga: floor(-2.3) hasilnya -3.
  • ceil(x): integer terkecil yang tidak lebih kecil dari x. Selalu bergerak ke arah plus tak hingga: ceil(-2.3) hasilnya -2.
  • round(x): integer terdekat, nilai setengah dibulatkan menjauhi nol: round(2.5) hasilnya 3, round(-2.5) hasilnya -3.
  • trunc(x): memotong bagian pecahan, bergerak ke arah nol: trunc(-2.7) hasilnya -2.

Keempatnya mengembalikan double. Cast ke int saat kamu butuh integer memang tidak masalah, tapi perhatikan bahwa (int)x sendiri melakukan pemotongan. Jadi (int)(x + 0.5) adalah idiom "pembulatan" lama, dan idiom itu salah untuk angka negatif. Pakai round().

fabs dan fmod

fabs adalah nilai mutlak untuk floating point. Memakai abs sebagai gantinya adalah kesalahan klasik, karena abs menerima int dan konversinya terjadi diam-diam:

% sama sekali tidak bekerja untuk double: itu operator integer dan compiler menolaknya. fmod(a, b) adalah pasangannya untuk floating point, dan tanda hasilnya mengikuti a.

fabs juga cara yang benar untuk membandingkan dua double, karena == pada floating point adalah jebakan:

0.1 dan 0.2 tidak bisa direpresentasikan secara tepat dalam biner, jadi jumlahnya sedikit meleset dari 0.3. Bandingkan dengan toleransi, jangan pernah dengan ==.

Trigonometri

Fungsi trigonometri bekerja dalam radian, bukan derajat. Ini sumber jawaban salah lain yang selalu bisa diandalkan.

Daftar lengkapnya: sin, cos, tan, inversnya asin, acos, atan, versi dua argumen atan2(y, x), dan keluarga hiperbolik sinh, cosh, tanh. Utamakan atan2(y, x) daripada atan(y / x) untuk sudut: fungsi itu menangani x == 0 dan menentukan kuadran dengan benar.

math.h mendefinisikan M_PI di kebanyakan sistem, tapi itu ekstensi POSIX, bukan C standar, jadi bisa saja tidak ada dengan -std=c17. Mendefinisikan konstantamu sendiri, seperti di atas, lebih portabel.

Logaritma dan eksponensial

Perhatikan jebakan penamaan yang terbawa dari matematika: log() adalah logaritma natural (basis e), bukan basis 10. Basis 10 adalah log10().

log(0.0) mengembalikan minus tak hingga dan log(-1.0) mengembalikan NaN. Keduanya tidak membuat program crash, artinya argumen yang salah bisa terbawa diam-diam di sepanjang perhitunganmu sampai ada yang mencetak nan.

INFINITY, NAN, dan cara mengeceknya

Floating point punya nilai yang bukan angka, dan math.h memberinya nama:

Baris terakhir itulah yang paling penting. Kamu tidak bisa menulis if (x == NAN): hasilnya selalu salah. Pakai isnan(x). (Identitas x != x hanya benar untuk NaN. Begitulah isnan sering diimplementasikan, dan itu cadangan yang praktis kalau suatu saat kamu bertemu toolchain yang tidak punya fungsi itu.)

Nilai tak hingga muncul dari overflow (exp(1000.0)) dan dari pembagian double bukan nol dengan nol, yang, berbeda dengan pembagian integer dengan nol, terdefinisi dan tidak membuat program crash.

Varian float dan long double

Setiap fungsi punya tiga bentuk: versi double, versi float berakhiran f, dan versi long double berakhiran l.

float       sqrtf(float x);
double      sqrt(double x);
long double sqrtl(long double x);

Pakai versi double biasa kecuali kamu punya alasan yang sudah diukur untuk tidak memakainya. double adalah tipe yang dipromosikan C secara bawaan, dan mencampur varian float biasanya lebih banyak memakan biaya konversi daripada yang dihemat. Satu-satunya tempat akhiran itu penting adalah saat perhitungan yang hanya memakai float sangat menentukan performa di hardware tanpa dukungan double.

Contoh praktis

Menggabungkan beberapa fungsi ini: jarak antara dua titik dan akar persamaan kuadrat:

Perhatikan fabs(disc) < 1e-12, bukan disc == 0.0. Ini aturan toleransi yang sama seperti tadi, diterapkan di tempat diskriminan yang dihitung dari nilai hasil pengukuran hampir tidak pernah tepat jatuh di nol.

<math.h> hanyalah satu header dalam perangkat yang lebih besar; halaman library standar memetakan sisanya, dan angka acak membahas rand, yang ada di <stdlib.h>, bukan di sini.

Pertanyaan yang sering diajukan

Bagaimana cara memakai fungsi matematika di C?

Tambahkan #include <math.h> di bagian atas file lalu panggil fungsinya: sqrt(16.0), pow(2.0, 10.0), fabs(-3.5). Fungsi-fungsi ini menerima dan mengembalikan double. Di Linux kamu juga harus me-link library matematika dengan -lm: gcc program.c -o program -lm.

Kenapa muncul 'undefined reference to sqrt' saat kompilasi?

Header sudah memberi compiler deklarasinya, tapi implementasinya ada di library terpisah yang tidak otomatis diambil linker di Linux. Tambahkan -lm di akhir perintah: gcc program.c -o program -lm. Di macOS dan dengan MinGW di Windows, kode matematika sudah jadi bagian library C standar, jadi tidak perlu flag apa pun.

Apa bedanya abs dan fabs di C?

abs() dari <stdlib.h> menerima int dan mengembalikan int. fabs() dari <math.h> menerima double dan mengembalikan double. Memanggil abs(-3.7) mengubah argumennya ke int lebih dulu dan menghasilkan 3, diam-diam membuang pecahannya. Pakai fabs untuk nilai floating point.

Bagaimana cara mengecek NaN di C?

Pakai isnan(x) dari <math.h>. Kamu tidak bisa mengujinya dengan x == NAN, karena NaN tidak sama dengan apa pun, termasuk dirinya sendiri. Keanehan itu justru menjadi uji cadangannya: x != x hanya bernilai benar untuk NaN. Pakai isinf(x) untuk tak hingga dan isfinite(x) untuk memastikan angkanya angka biasa.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai