Kerangka dan I/O sudah siap; kini kita mengisi tubuh program — fungsi kalkulator_hitung yang benar-benar menjumlahkan, mengurangkan, mengalikan, dan membagi dua bilangan. Pada bagian ini kita memakai tipe double agar pembagian dapat menghasilkan pecahan seperti 10 / 4 = 2.5, dan kita menambahkan pengecekan khusus untuk pembagian dengan nol agar program tidak crash atau menghasilkan inf.
Konsep #
Operasi aritmetika di C menggunakan operator +, -, *, / yang sama untuk int, float, dan double. Yang membedakan adalah tipe operand: bila kedua operand bertipe integer, pembagian / melakukan integer division sehingga 10 / 4 menghasilkan 2, bukan 2.5. Karena kita ingin presisi pecahan, seluruh perhitungan dilakukan dalam double.
Satu kasus khusus adalah pembagian dengan nol. Pada bilangan floating-point IEEE-754, x / 0.0 menghasilkan +inf atau -inf (atau NaN bila x juga nol), bukan crash. Namun inf tidak berguna bagi pengguna kalkulator. Maka kita memeriksa b == 0.0 sebelum melakukan pembagian dan memulangkan CALC_ERR_BAGI_NOL agar pemanggil dapat menampilkan pesan yang berarti.
Pemilihan operasi dilakukan dengan switch pada enum Operasi. Switch di C lebih cocok untuk dispatch berbasis nilai diskret daripada rantai if-else if yang panjang.
Implementasi #
Fungsi kalkulator_hitung memeriksa pointer hasil terlebih dahulu, lalu beralih berdasarkan op:
CalcStatus kalkulator_hitung(Operasi op, double a, double b, double *hasil) {
if (hasil == 0) return CALC_ERR_OP_TIDAK_DIKETAHUI;
switch (op) {
case OP_TAMBAH: *hasil = a + b; return CALC_OK;
case OP_KURANG: *hasil = a - b; return CALC_OK;
case OP_KALI: *hasil = a * b; return CALC_OK;
case OP_BAGI:
if (b == 0.0) return CALC_ERR_BAGI_NOL;
*hasil = a / b;
return CALC_OK;
default: return CALC_ERR_OP_TIDAK_DIKETAHUI;
}
}Pemanggil main.c memeriksa status dan menampilkan hasil dalam format a simbol b = hasil. Label dan simbol diambil dari dua fungsi pembantu:
const char *kalkulator_simbol(Operasi op) {
switch (op) {
case OP_TAMBAH: return "+";
case OP_KURANG: return "-";
case OP_KALI: return "*";
case OP_BAGI: return "/";
default: return "?";
}
}Kode Lengkap #
Berkas calculator.c lengkap:
#include "calculator.h"
CalcStatus kalkulator_hitung(Operasi op, double a, double b, double *hasil) {
if (hasil == 0) return CALC_ERR_OP_TIDAK_DIKETAHUI;
switch (op) {
case OP_TAMBAH: *hasil = a + b; return CALC_OK;
case OP_KURANG: *hasil = a - b; return CALC_OK;
case OP_KALI: *hasil = a * b; return CALC_OK;
case OP_BAGI:
if (b == 0.0) return CALC_ERR_BAGI_NOL;
*hasil = a / b;
return CALC_OK;
default: return CALC_ERR_OP_TIDAK_DIKETAHUI;
}
}
const char *kalkulator_label(Operasi op) {
switch (op) {
case OP_TAMBAH: return "Tambah";
case OP_KURANG: return "Kurang";
case OP_KALI: return "Kali";
case OP_BAGI: return "Bagi";
default: return "Tidak Diketahui";
}
}
const char *kalkulator_simbol(Operasi op) {
switch (op) {
case OP_TAMBAH: return "+";
case OP_KURANG: return "-";
case OP_KALI: return "*";
case OP_BAGI: return "/";
default: return "?";
}
}Potongan main.c yang memanggil fungsi tersebut:
Operasi op = (Operasi)pilihan;
double hasil = 0.0;
CalcStatus s = kalkulator_hitung(op, a, b, &hasil);
if (s == CALC_OK) {
printf("Hasil: %g %s %g = %g\n",
a, kalkulator_simbol(op), b, hasil);
} else if (s == CALC_ERR_BAGI_NOL) {
printf("Error: tidak bisa membagi dengan nol.\n");
}Pengujian #
Kompilasi dan jalankan program dengan urutan operasi yang berbeda:
gcc -std=c11 -Wall -Wextra -o /tmp/p1b3 main.c calculator.c -lm
printf '4\n10\n4\ny\n3\n2.5\n4\ny\n2\n10\n3\nt\n' | /tmp/p1b3Output yang Dihasilkan:
=== Kalkulator Terminal ===
1. Tambah
2. Kurang
3. Kali
4. Bagi
5. Keluar
Pilih operasi (1-5): Angka pertama: Angka kedua: Hasil: 10 / 4 = 2.5
Hitung lagi? (y/t): === Kalkulator Terminal ===
1. Tambah
2. Kurang
3. Kali
4. Bagi
5. Keluar
Pilih operasi (1-5): Angka pertama: Angka kedua: Hasil: 2.5 * 4 = 10
Hitung lagi? (y/t): === Kalkulator Terminal ===
1. Tambah
2. Kurang
3. Kali
4. Bagi
5. Keluar
Pilih operasi (1-5): Angka pertama: Angka kedua: Hasil: 10 - 3 = 7
Hitung lagi? (y/t): Terima kasih telah menggunakan Kalkulator Terminal.Sekarang uji pembagian dengan nol:
printf '4\n5\n0\nt\n' | /tmp/p1b3Output yang Dihasilkan:
=== Kalkulator Terminal ===
1. Tambah
2. Kurang
3. Kali
4. Bagi
5. Keluar
Pilih operasi (1-5): Angka pertama: Angka kedua: Error: tidak bisa membagi dengan nol.
Hitung lagi? (y/t): Terima kasih telah menggunakan Kalkulator Terminal.Mari kita telaah baris-baris kunci pada kalkulator_hitung:
- Baris
if (hasil == 0) return ...;: pointer NULL berarti pemanggil lupa menyediakan variabel output. Kita tidak dapat menulis hasil, jadi pulangkan kode error. - Baris
switch (op): setiapcasemenulis hasil dan langsungreturn, sehingga tidak perlubreaksetelahreturn. Polareturndi akhir case ini lebih ringkas daripadabreakdiikutireturndi akhir fungsi. - Baris
if (b == 0.0) return CALC_ERR_BAGI_NOL;: pembandingan floating-point dengan literal0.0aman karena hanya0.0yang persis sama dengan0.0. Untuk pembandingan nilai floating-point umum, gunakan toleransi epsilon — di sini tidak diperlukan karena kita hanya mengecek nol. - Baris
default: return CALC_ERR_OP_TIDAK_DIKETAHUI;: walaupun enum hanya punya empat anggota,defaultmelindungi dari nilai enum yang tidak terduga (misal dari casting integer asal).
Bagian 4 akan memperketat validasi input agar pesan error lebih informatif.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
calculator.c
Unduh berkas/* calculator.c -- implementasi logika perhitungan kalkulator terminal. */
#include "calculator.h"
/* Header ini diperlukan untuk strcmp di kalkulator_label. */
#include <string.h>
CalcStatus kalkulator_hitung(Operasi op, double a, double b, double *hasil) {
/* Pastikan pointer hasil tidak NULL sebelum menulis. */
if (hasil == 0) {
return CALC_ERR_OP_TIDAK_DIKETAHUI;
}
switch (op) {
case OP_TAMBAH:
*hasil = a + b;
return CALC_OK;
case OP_KURANG:
*hasil = a - b;
return CALC_OK;
case OP_KALI:
*hasil = a * b;
return CALC_OK;
case OP_BAGI:
if (b == 0.0) {
return CALC_ERR_BAGI_NOL;
}
*hasil = a / b;
return CALC_OK;
default:
return CALC_ERR_OP_TIDAK_DIKETAHUI;
}
}
const char *kalkulator_label(Operasi op) {
switch (op) {
case OP_TAMBAH: return "Tambah";
case OP_KURANG: return "Kurang";
case OP_KALI: return "Kali";
case OP_BAGI: return "Bagi";
default: return "Tidak Diketahui";
}
}
const char *kalkulator_simbol(Operasi op) {
switch (op) {
case OP_TAMBAH: return "+";
case OP_KURANG: return "-";
case OP_KALI: return "*";
case OP_BAGI: return "/";
default: return "?";
}
}
main.c
Unduh berkas/* main.c -- titik masuk kalkulator terminal.
*
* Memisahkan I/O (di main.c) dari logika perhitungan (di calculator.c)
* supaya calculator.c dapat diuji tanpa membaca input pengguna.
*/
#include "calculator.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Konstanta menu untuk pilihan keluar. */
#define MENU_KELUAR 5
/* Menampilkan daftar menu utama ke stdout. */
static void tampilkan_menu(void) {
printf("=== Kalkulator Terminal ===\n");
printf("1. Tambah\n");
printf("2. Kurang\n");
printf("3. Kali\n");
printf("4. Bagi\n");
printf("5. Keluar\n");
}
/* Membaca satu baris input dan memparse bilangan double.
*
* Mengembalikan 1 bila berhasil, 0 bila input tidak terurai sebagai angka.
*/
static int baca_angka(const char *label, double *out) {
char buffer[128];
printf("%s: ", label);
if (fgets(buffer, sizeof(buffer), stdin) == 0) {
return 0;
}
/* fgets menyimpan newline; kita andalkan strtod untuk berhenti di sana. */
char *sisa = 0;
double nilai = strtod(buffer, &sisa);
/* Bila seluruh isi buffer adalah whitespace atau kosong, gagal. */
if (sisa == buffer) {
return 0;
}
*out = nilai;
return 1;
}
/* Membaca pilihan menu 1-5 dari input. */
static int baca_pilihan(void) {
char buffer[128];
printf("Pilih operasi (1-5): ");
if (fgets(buffer, sizeof(buffer), stdin) == 0) {
return MENU_KELUAR;
}
/* Ambil karakter pertama yang bukan whitespace. */
char *p = buffer;
while (*p == ' ' || *p == '\t') {
p++;
}
if (*p < '1' || *p > '5') {
return 0;
}
return *p - '0';
}
/* Menanyakan apakah pengguna ingin menghitung lagi. */
static int tanya_lagi(void) {
char buffer[16];
printf("Hitung lagi? (y/t): ");
if (fgets(buffer, sizeof(buffer), stdin) == 0) {
return 0;
}
return buffer[0] == 'y' || buffer[0] == 'Y';
}
int main(void) {
int lanjut = 1;
while (lanjut) {
tampilkan_menu();
int pilihan = baca_pilihan();
if (pilihan == MENU_KELUAR) {
break;
}
if (pilihan < 1 || pilihan > 4) {
printf("Pilihan tidak dikenal.\n");
continue;
}
double a = 0.0;
double b = 0.0;
if (!baca_angka("Angka pertama", &a)) {
printf("Input angka pertama tidak valid.\n");
continue;
}
if (!baca_angka("Angka kedua", &b)) {
printf("Input angka kedua tidak valid.\n");
continue;
}
Operasi op = (Operasi)pilihan;
double hasil = 0.0;
CalcStatus status = kalkulator_hitung(op, a, b, &hasil);
if (status == CALC_OK) {
printf("Hasil: %g %s %g = %g\n",
a, kalkulator_simbol(op), b, hasil);
} else if (status == CALC_ERR_BAGI_NOL) {
printf("Error: tidak bisa membagi dengan nol.\n");
} else {
printf("Error: operasi tidak dikenal.\n");
}
lanjut = tanya_lagi();
}
printf("Terima kasih telah menggunakan Kalkulator Terminal.\n");
return 0;
}
tests.c
Unduh berkas/* tests.c -- rangkaian test sederhana untuk modul calculator.
*
* Tiap test memanggil kalkulator_hitung lalu membandingkan hasil dengan
* nilai yang diharapkan. Jika ada test yang gagal, program keluar dengan
* kode bukan nol.
*/
#include "calculator.h"
#include <stdio.h>
#include <math.h>
#include <string.h>
static int test_lulus = 0;
static int test_gagal = 0;
/* Pembanding aman untuk bilangan double. */
static int hampir_sama(double a, double b) {
return fabs(a - b) < 1e-9;
}
/* Macro untuk mencatat dan melaporkan hasil test. */
#define PERIKSA(kondisi, nama) \
do { \
if (kondisi) { \
test_lulus++; \
printf("[LULUS] %s\n", nama); \
} else { \
test_gagal++; \
printf("[GAGAL] %s\n", nama); \
} \
} while (0)
static void test_tambah(void) {
double hasil = 0.0;
CalcStatus s = kalkulator_hitung(OP_TAMBAH, 5.0, 3.0, &hasil);
PERIKSA(s == CALC_OK && hampir_sama(hasil, 8.0), "Tambah 5 + 3 = 8");
}
static void test_kurang(void) {
double hasil = 0.0;
CalcStatus s = kalkulator_hitung(OP_KURANG, 10.0, 4.0, &hasil);
PERIKSA(s == CALC_OK && hampir_sama(hasil, 6.0), "Kurang 10 - 4 = 6");
}
static void test_kali(void) {
double hasil = 0.0;
CalcStatus s = kalkulator_hitung(OP_KALI, 2.5, 4.0, &hasil);
PERIKSA(s == CALC_OK && hampir_sama(hasil, 10.0), "Kali 2.5 * 4 = 10");
}
static void test_bagi(void) {
double hasil = 0.0;
CalcStatus s = kalkulator_hitung(OP_BAGI, 10.0, 4.0, &hasil);
PERIKSA(s == CALC_OK && hampir_sama(hasil, 2.5), "Bagi 10 / 4 = 2.5");
}
static void test_bagi_nol(void) {
double hasil = 0.0;
CalcStatus s = kalkulator_hitung(OP_BAGI, 5.0, 0.0, &hasil);
PERIKSA(s == CALC_ERR_BAGI_NOL, "Bagi dengan nol menolak");
}
static void test_label_dan_simbol(void) {
PERIKSA(strcmp(kalkulator_label(OP_TAMBAH), "Tambah") == 0,
"Label OP_TAMBAH = \"Tambah\"");
PERIKSA(strcmp(kalkulator_simbol(OP_BAGI), "/") == 0,
"Simbol OP_BAGI = \"/\"");
}
int main(void) {
test_tambah();
test_kurang();
test_kali();
test_bagi();
test_bagi_nol();
test_label_dan_simbol();
printf("\nRingkasan: %d lulus, %d gagal\n", test_lulus, test_gagal);
return test_gagal == 0 ? 0 : 1;
}