Dua chapter terakhir memakai epsilon dan max_digits10 seadanya; sekarang saatnya membongkar seluruh kotak peralatannya: std::numeric_limits dari header <limits> — template class yang menyimpan karakteristik setiap tipe aritmetika dalam bentuk konstanta compile-time. Daripada menghafal bahwa int berhenti di 2147483647 atau double punya 17 digit round-trip, kode Anda bisa bertanya ke tipe sendiri — dan tetap benar begitu tipe diganti ke long long atau platform lain. Chapter ini memetakan anggota-anggota pentingnya (batas atas dan bawah, epsilon, round_error, infinity, quiet_NaN, denorm_min, has_infinity), membuktikan jebakan klasik min() yang artinya berbeda antara integer dan floating-point, serta menjernihkan pasangan yang paling sering tertukar: digits10 versus max_digits10.
Peta Anggota numeric_limits #
| Anggota | Arti | int | double |
|---|---|---|---|
max() | nilai terbesar yang bisa disimpan | 2147483647 | ≈ 1,80 × 10³⁰⁸ |
lowest() | nilai terkecil paling negatif | -2147483648 | ≈ -1,80 × 10³⁰⁸ |
min() | nilai terkecil positif (floating) / sama dengan lowest (integer) | -2147483648 | ≈ 2,23 × 10⁻³⁰⁸ |
denorm_min() | nilai positif terhalus, termasuk denormal | 0 | ≈ 4,94 × 10⁻³²⁴ |
epsilon() | jarak 1.0 ke nilai berikutnya | 0 | ≈ 2,22 × 10⁻¹⁶ |
round_error() | galat pembulatan terbesar (½ ulp) | 0 | 0,5 |
digits10 | digit desimal yang dijamin tersimpan | 9 | 15 |
max_digits10 | digit desimal untuk round-trip penuh | — | 17 |
has_infinity / infinity() | dukungan dan nilai infinity | false / 0 | true / inf |
quiet_NaN() | NaN “tenang” yang menular tanpa crash | 0 | nan |
Semua anggota di atas adalah static constexpr — nilainya melekat pada tipe, bukan objek, sehingga aksesnya ditulis std::numeric_limits<double>::max() tanpa instansiasi. Untuk tipe non-aritmetika (misalnya class buatan Anda), template primernya sengaja mengembalikan nilai netral seperti 0 dan false; spesialisasi khusus hanya ada untuk tipe fundamental.
Membaca Batas dari Kode #
Program berikut memanggil semua anggota di atas dan menguji dua perilaku yang tidak bisa ditebak dari namanya: denorm_min() yang habis menjadi nol saat dibagi dua, dan round-trip presisi yang menuntut max_digits10:
#include <iomanip>
#include <iostream>
#include <limits>
int main() {
std::cout << std::boolalpha;
// 1. Integer: batas simetris, tanpa epsilon bermakna
std::cout << "int max : " << std::numeric_limits<int>::max() << "\n";
std::cout << "int lowest : " << std::numeric_limits<int>::lowest() << "\n";
std::cout << "ll max : "
<< std::numeric_limits<long long>::max() << "\n";
// 2. Floating: empat "batas" yang berbeda makna
std::cout << "dbl max : " << std::numeric_limits<double>::max() << "\n";
std::cout << "dbl lowest : "
<< std::numeric_limits<double>::lowest() << "\n";
std::cout << "dbl min : " << std::numeric_limits<double>::min() << "\n";
std::cout << "dbl denorm : "
<< std::numeric_limits<double>::denorm_min() << "\n";
// 3. Presisi: epsilon, round_error, digits10 vs max_digits10
std::cout << "dbl epsilon : "
<< std::numeric_limits<double>::epsilon() << "\n";
std::cout << "dbl round : "
<< std::numeric_limits<double>::round_error() << "\n";
std::cout << "digits10 : "
<< std::numeric_limits<double>::digits10 << "\n";
std::cout << "max_digits10: "
<< std::numeric_limits<double>::max_digits10 << "\n";
// 4. Nilai khusus
std::cout << "has_infinity: "
<< std::numeric_limits<double>::has_infinity << "\n";
std::cout << "infinity : "
<< std::numeric_limits<double>::infinity() << "\n";
std::cout << "quiet_NaN : "
<< std::numeric_limits<double>::quiet_NaN() << "\n";
// 5. denorm_min adalah nilai positif terhalus: setengahnya jatuh ke nol
double halus = std::numeric_limits<double>::denorm_min();
std::cout << "denorm/2 : " << halus / 2.0 << "\n";
// 6. Round-trip butuh max_digits10, bukan digits10
std::cout << std::setprecision(std::numeric_limits<double>::digits10);
std::cout << "0.1 @15 : " << 0.1 << "\n";
std::cout << std::setprecision(std::numeric_limits<double>::max_digits10);
std::cout << "0.1 @17 : " << 0.1 << "\n";
return 0;
}Output yang Dihasilkan:
int max : 2147483647
int lowest : -2147483648
ll max : 9223372036854775807
dbl max : 1.79769e+308
dbl lowest : -1.79769e+308
dbl min : 2.22507e-308
dbl denorm : 4.94066e-324
dbl epsilon : 2.22045e-16
dbl round : 0.5
digits10 : 15
max_digits10: 17
has_infinity: true
infinity : inf
quiet_NaN : nan
denorm/2 : 0
0.1 @15 : 0.1
0.1 @17 : 0.10000000000000001Bedah baris-per-baris:
- Baris 6 (
std::cout << std::boolalpha;): mengubah pencetakan boolean dari1/0menjaditrue/false, sehinggahas_infinityterbaca sebagai kata, bukan angka. - Baris 9-11 (
std::numeric_limits<int>::max() ... lowest()): batas integer simetris sempurna (2³¹ - 1 dan -2³¹), danlong longterbaca 9223372036854775807 — sekitar 9,2 triliun, seperti yang kita gunakan saat berdebat memilih tipe lebih besar di Chapter Integer Overflow. - Baris 15-18 (
max() ... lowest() ... min()): perhatikandbl min= 2,23 × 10⁻³⁰⁸ adalah nilai positif terkecil ternormalisasi, bukan nilai paling negatif. Inilah jebakan klasik: untuk floating-point,min()danlowest()berbeda; untuk integer keduanya sama. - Baris 19-20 (
denorm_min()): nilai positif paling halus di bawah batas normal — representasi denormal yang menurunkan jumlah bit efektif mantissa demi mendekati nol secara bertahap (gradual underflow). - Baris 23-26 (
epsilon() ... round_error()): epsilon 2,22 × 10⁻¹⁶ adalah ulp di sekitar 1.0 yang kita pakai untuk toleransi relatif;round_error= 0,5 adalah galat pembulatan maksimum (setengah ulp) untuk mode pembulatan ke terdekat. - Baris 33-38 (
has_infinity ... infinity() ... quiet_NaN()): platform ini laporanhas_infinity = truedan nilai khususnya tercetakinfdannan— konsisten dengan hasil 1.0/0.0 dan 0.0/0.0 pada Chapter Floating Point Representation. - Baris 41-42 (
double halus = ...denorm_min(); ... halus / 2.0): membagi dua nilai terhalus itu jatuh ke0— tepat di bawah batas terakhir representasi. Ini bukan crash maupun NaN; underflow bertahap memilih nilai terdekat yang tersedia: nol. - Baris 45-48 (
setprecision(...digits10 ... max_digits10)): dengan 15 digit, 0,1 tercetak0.1— cukup rapi untuk manusia; dengan 17 digit tercetak0.10000000000000001, satu-satunya bentuk yang menjamin membaca teks kembali ke bit double yang identik.
digits10 vs max_digits10: Dua Janji Berbeda #
digits10 = 15 menjanjikan: bila Anda menyimpan double lalu mencetaknya dengan 15 digit, nilainya masih “berada di lingkaran yang benar” — terbaca kembali menjadi double yang sama. max_digits10 = 17 menjanjikan yang lebih kuat: setiap double apa pun dapat dibedakan dari tetangganya dalam teks. Keduanya benar sekaligus karena beberapa nilai double butuh 17 digit agar tidak bertabrakan dengan tetangganya saat ditulis. Konsekuensi praktisnya sederhana: cetak untuk manusia → 15 digit (atau default 6); simpan ke file/JSON untuk dibaca kembali oleh mesin → 17 digit, selalu.
Dengan peta batas tipe yang lengkap, bagian numerik kita beralih dari angka yang pasti ke angka yang sengaja dibuat tidak pasti: bilangan acak. Header <random> menyediakan mesin dan distribusi yang berkualitas jauh di atas rand() warisan — dan yang lebih penting untuk pengujian, urutannya bisa dibuat persis ulang dengan seed tetap.
Rujukan resmi untuk topik Chapter ini: std::numeric_limits dan overview numerik C++ di cppreference.