Header <limits.h> dan <float.h> menyediakan konstanta makros yang mendeskripsikan rentang dan presisi tipe data primitif di C. Karena ukuran int, long, dan double tidak dijamin sama di semua platform, kita tidak boleh hard-code angka seperti 2147483647 di kode—harus selalu memakai INT_MAX. Demikian pula untuk tipe floating-point, DBL_EPSILON memberi tahu berapa presisi yang dapat diandalkan, dan FLT_MAX memberi tahu batas atas sebelum terjadi overflow ke Infinity. Pada chapter ini kita akan meninjau konstanta-konstanta utama, perilaku overflow integer vs floating-point, dan cara memilih tipe yang tepat berdasarkan rentang data.

Konstanta limits.h untuk Tipe Integer #

Header <limits.h> mengekspos konstanta untuk setiap tipe integer primitif: CHAR_BIT (jumlah bit per byte, selalu 8 di sistem modern), SCHAR_MIN/SCHAR_MAX untuk signed char, UCHAR_MAX untuk unsigned char, SHRT_*/USHRT_* untuk short, INT_*/UINT_* untuk int, LONG_*/ULONG_* untuk long, LLONG_*/ULLONG_* untuk long long.

c
#include <stdio.h>
#include <limits.h>
#include <float.h>

int main(void) {
    /* Batas tipe integer dari limits.h */
    printf("=== limits.h (tipe integer) ===\n");
    printf("CHAR_BIT    = %d\n", CHAR_BIT);
    printf("SCHAR_MIN   = %d\n", SCHAR_MIN);
    printf("SCHAR_MAX   = %d\n", SCHAR_MAX);
    printf("UCHAR_MAX   = %u\n", (unsigned)UCHAR_MAX);
    printf("SHRT_MIN    = %d\n", SHRT_MIN);
    printf("SHRT_MAX    = %d\n", SHRT_MAX);
    printf("USHRT_MAX   = %u\n", (unsigned)USHRT_MAX);
    printf("INT_MIN     = %d\n", INT_MIN);
    printf("INT_MAX     = %d\n", INT_MAX);
    printf("UINT_MAX    = %u\n", UINT_MAX);
    printf("LONG_MIN    = %ld\n", LONG_MIN);
    printf("LONG_MAX    = %ld\n", LONG_MAX);
    printf("ULONG_MAX   = %lu\n", ULONG_MAX);
    printf("LLONG_MIN   = %lld\n", LLONG_MIN);
    printf("LLONG_MAX   = %lld\n", LLONG_MAX);
    printf("ULLONG_MAX  = %llu\n", ULLONG_MAX);

    return 0;
}

Output yang Dihasilkan:

text
=== limits.h (tipe integer) ===
CHAR_BIT    = 8
SCHAR_MIN   = -128
SCHAR_MAX   = 127
UCHAR_MAX   = 255
SHRT_MIN    = -32768
SHRT_MAX    = 32767
USHRT_MAX   = 65535
INT_MIN     = -2147483648
INT_MAX     = 2147483647
UINT_MAX    = 4294967295
LONG_MIN    = -9223372036854775808
LONG_MAX    = 9223372036854775807
ULONG_MAX   = 18446744073709551615
LLONG_MIN   = -9223372036854775808
LLONG_MAX   = 9223372036854775807
ULLONG_MAX  = 18446744073709551615

Mari kita telaah baris-baris kode di atas:

  1. Baris 7 (CHAR_BIT): jumlah bit dalam satu byte. Di semua platform modern nilainya 8, tetapi standard C hanya menjamin CHAR_BIT >= 8. Beberapa arsitektur eksotis (misal DSP lama) memiliki CHAR_BIT = 16 atau 24.
  2. Baris 8-10 (SCHAR_*, UCHAR_MAX): rentang signed char adalah -128 sampai 127; unsigned char dari 0 sampai 255. Makro (unsigned)UCHAR_MAX di-cast ke unsigned karena printf %u membutuhkan unsigned int.
  3. Baris 14-15 (INT_MIN, INT_MAX): int 32-bit di sistem ini, sehingga rentangnya -2147483648 sampai 2147483647. Di platform 16-bit, int hanya 16 bit dengan rentang -32768 sampai 32767.
  4. Baris 18-19 (LONG_*): pada LP64 (Linux x86_64), long adalah 64-bit. Pada LLP64 (Windows x64), long hanya 32-bit. Inilah alasan utama mengapa kita tidak boleh mengasumsikan ukuran long—selalu periksa sizeof(long) atau gunakan tipe stdint.h (lihat chapter terpisah).
  5. Baris 22 (ULLONG_MAX): unsigned long long 64-bit pada platform ini—maksimum 18446744073709551615 (2^64 - 1).

Konstanta float.h untuk Tipe Floating-Point #

Header <float.h> mengekspos konstanta presisi tipe floating-point. Yang paling sering dipakai: FLT_DIG/DBL_DIG/LDBL_DIG (jumlah digit desimal yang dapat diandalkan), FLT_MIN/FLT_MAX/DBL_MIN/DBL_MAX (rentang nilai positif), FLT_EPSILON/DBL_EPSILON (selisih terkecil yang dapat dibedakan dari 1.0), FLT_MANT_DIG/DBL_MANT_DIG (jumlah bit biner di significand).

c
#include <stdio.h>
#include <float.h>

int main(void) {
    /* Batas tipe floating point dari float.h */
    printf("=== float.h (tipe float) ===\n");
    printf("FLT_DIG     = %d  (digit desimal presisi float)\n", FLT_DIG);
    printf("DBL_DIG     = %d  (digit desimal presisi double)\n", DBL_DIG);
    printf("LDBL_DIG    = %d\n", LDBL_DIG);

    printf("FLT_MIN     = %e (float positif terkecil)\n", FLT_MIN);
    printf("FLT_MAX     = %e (float positif terbesar)\n", FLT_MAX);
    printf("DBL_MIN     = %e\n", DBL_MIN);
    printf("DBL_MAX     = %e\n", DBL_MAX);

    printf("FLT_EPSILON = %e (selisih 1.0 ke float terdekat)\n", FLT_EPSILON);
    printf("DBL_EPSILON = %e\n", DBL_EPSILON);

    printf("FLT_MANT_DIG= %d (digit biner significand float)\n", FLT_MANT_DIG);
    printf("DBL_MANT_DIG= %d\n", DBL_MANT_DIG);

    return 0;
}

Output yang Dihasilkan:

text
=== float.h (tipe float) ===
FLT_DIG     = 6  (digit desimal presisi float)
DBL_DIG     = 15  (digit desimal presisi double)
LDBL_DIG    = 18
FLT_MIN     = 1.175494e-38 (float positif terkecil)
FLT_MAX     = 3.402823e+38 (float positif terbesar)
DBL_MIN     = 2.225074e-308
DBL_MAX     = 1.797693e+308
FLT_EPSILON = 1.192093e-07 (selisih 1.0 ke float terdekat)
DBL_EPSILON = 2.220446e-16
FLT_MANT_DIG= 24 (digit biner significand float)
DBL_MANT_DIG= 53

Konstanta FLT_DIG = 6 berarti float hanya dapat diandalkan untuk 6 digit desimal—angka ke-7 mungkin berubah karena pembulatan. DBL_DIG = 15 memberi 15 digit untuk double, dan LDBL_DIG = 18 untuk long double (80-bit extended pada x86). FLT_EPSILON adalah machine epsilon untuk float: selisih terkecil antara 1.0f dan float berikutnya yang dapat direpresentasikan. DBL_EPSILON jauh lebih kecil karena double punya 53 bit significand. Rentang nilai float positif: dari 1.175494e-38 (paling kecil, bukan nol) sampai 3.402823e+38 (paling besar). Untuk double, rentang jauh lebih luas: dari 2.225074e-308 sampai 1.797693e+308.

Perilaku Overflow: Signed vs Unsigned vs Floating-Point #

Perilaku overflow berbeda untuk setiap kategori tipe. Integer signed overflow adalah undefined behavior di C—kompiler bebas mengasumsikan tidak terjadi dan hasilnya bisa tak terduga. Integer unsigned overflow well-defined sebagai wrap-around modular (aritmatika modulo 2^N). Floating-point overflow menghasilkan nilai Infinity (positive atau negative) yang ditangani IEEE-754.

c
#include <stdio.h>
#include <limits.h>
#include <float.h>
#include <math.h>

int main(void) {
    /* Demonstrasikan integer overflow: hasil tidak terdefinisi untuk signed! */
    int a = INT_MAX;
    printf("INT_MAX       = %d\n", a);
    printf("INT_MAX + 1   = %d (undefined behavior untuk signed!)\n", a + 1);

    /* Unsigned overflow: well-defined, wrap-around modular */
    unsigned u = UINT_MAX;
    printf("\nUINT_MAX      = %u\n", u);
    printf("UINT_MAX + 1  = %u (well-defined wrap-around)\n", u + 1);

    /* Demonstrasikan FLT_EPSILON: batas presisi float */
    float f = 1.0f;
    float eps = 1.0f;
    int n = 0;
    while (f + eps != f) {
        eps /= 2.0f;
        n++;
    }
    eps *= 2.0f;   /* EPSILON terkecil yang masih mengubah f */
    printf("\nDihitung FLT_EPSILON ~ %e (dalam %d iterasi)\n", eps, n);
    printf("Header  FLT_EPSILON   = %e\n", FLT_EPSILON);

    /* Demonstrasikan: setelah FLT_MAX, nilai menjadi infinity */
    float big = FLT_MAX;
    printf("\nFLT_MAX        = %e\n", big);
    printf("FLT_MAX * 2.0  = %e (overflow ke infinity)\n", big * 2.0f);

    return 0;
}

Output yang Dihasilkan:

text
INT_MAX       = 2147483647
INT_MAX + 1   = -2147483648 (undefined behavior untuk signed!)

UINT_MAX      = 4294967295
UINT_MAX + 1  = 0 (well-defined wrap-around)

Dihitung FLT_EPSILON ~ 1.192093e-07 (dalam 24 iterasi)
Header  FLT_EPSILON   = 1.192093e-07

FLT_MAX        = 3.402823e+38
FLT_MAX * 2.0  = inf (overflow ke infinity)

Loop while (f + eps != f) mencari EPSILON eksperimental: kita mulai dengan eps = 1.0f, lalu bagi dua sampai f + eps tidak lagi berbeda dari f sendiri. EPSILON terkecil yang masih mengubah f dikembalikan dengan mengalikan eps dengan 2 (karena kita sudah membagi satu langkah terlalu jauh). Hasilnya 1.192093e-07 cocok persis dengan FLT_EPSILON di header—dalam 24 iterasi (sesuai FLT_MANT_DIG = 24 bit significand). Untuk unsigned, overflow well-defined: UINT_MAX + 1 menjadi 0 karena aritmatika modulo 2^32. Untuk floating-point, FLT_MAX * 2.0 menghasilkan inf tanpa undefined behavior—IEEE-754 menangani kasus ini secara formal.