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:

c
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:

c
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:

c
#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:

c
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:

bash
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/p1b3

Output yang Dihasilkan:

text
=== 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:

bash
printf '4\n5\n0\nt\n' | /tmp/p1b3

Output yang Dihasilkan:

text
=== 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:

  1. Baris if (hasil == 0) return ...;: pointer NULL berarti pemanggil lupa menyediakan variabel output. Kita tidak dapat menulis hasil, jadi pulangkan kode error.
  2. Baris switch (op): setiap case menulis hasil dan langsung return, sehingga tidak perlu break setelah return. Pola return di akhir case ini lebih ringkas daripada break diikuti return di akhir fungsi.
  3. Baris if (b == 0.0) return CALC_ERR_BAGI_NOL;: pembandingan floating-point dengan literal 0.0 aman karena hanya 0.0 yang persis sama dengan 0.0. Untuk pembandingan nilai floating-point umum, gunakan toleransi epsilon — di sini tidak diperlukan karena kita hanya mengecek nol.
  4. Baris default: return CALC_ERR_OP_TIDAK_DIKETAHUI;: walaupun enum hanya punya empat anggota, default melindungi 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
c
/* 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
c
/* 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
c
/* 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;
}