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.
#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:
=== 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 = 18446744073709551615Mari kita telaah baris-baris kode di atas:
- Baris 7 (
CHAR_BIT): jumlah bit dalam satu byte. Di semua platform modern nilainya8, tetapi standard C hanya menjaminCHAR_BIT >= 8. Beberapa arsitektur eksotis (misal DSP lama) memilikiCHAR_BIT = 16atau24. - Baris 8-10 (
SCHAR_*,UCHAR_MAX): rentangsigned charadalah-128sampai127;unsigned chardari0sampai255. Makro(unsigned)UCHAR_MAXdi-cast keunsignedkarenaprintf%umembutuhkanunsigned int. - Baris 14-15 (
INT_MIN,INT_MAX):int32-bit di sistem ini, sehingga rentangnya-2147483648sampai2147483647. Di platform 16-bit,inthanya 16 bit dengan rentang-32768sampai32767. - Baris 18-19 (
LONG_*): pada LP64 (Linux x86_64),longadalah 64-bit. Pada LLP64 (Windows x64),longhanya 32-bit. Inilah alasan utama mengapa kita tidak boleh mengasumsikan ukuranlong—selalu periksasizeof(long)atau gunakan tipestdint.h(lihat chapter terpisah). - Baris 22 (
ULLONG_MAX):unsigned long long64-bit pada platform ini—maksimum18446744073709551615(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).
#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:
=== 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= 53Konstanta 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.
#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:
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.