Fundamental Types: int, double, bool, char
Every value in a C++ program has a type — a description of what kind of data it represents and how much memory it occupies. The fundamental (or built-in) types are the most basic building blocks: integers for whole numbers, floating-point types for fractional numbers, bool for true/false, and char for individual characters. Understanding these types — and especially their limits — prevents subtle bugs that only appear with large numbers or specific input values.
Integer types
An integer type stores a whole number (no fractional part). C++ provides several integer types that differ in size and whether they can represent negative numbers. The size determines the range: a type that occupies n bits can represent 2n distinct values. The most common integer type is int, which on virtually all modern platforms is 32 bits — giving a range of roughly −2.1 billion to +2.1 billion.
| Type | Typical size | Range | Use for |
|---|---|---|---|
| short | 16 bits | −32 768 … 32 767 | Tight memory budgets |
| int | 32 bits | −2 147 483 648 … 2 147 483 647 | Default whole-number arithmetic |
| long | 32 or 64 bits (platform) | At least as wide as int | Slightly wider integers |
| long long | 64 bits | −9.2 × 10¹⁸ … 9.2 × 10¹⁸ | Very large integers (file sizes, IDs) |
| unsigned int | 32 bits | 0 … 4 294 967 295 | Counts, indices (never negative) |
| std::size_t | 64 bits on 64-bit systems | 0 … 2⁶⁴−1 | Sizes and indices into containers |
int apples = 5; int temperature = -3; unsigned int count = 0; long long fileSize = 5'000'000'000LL; // 5 GB — needs long long // Digit separators (C++14): use ' for readability int million = 1'000'000; long long big = 9'223'372'036'854'775'807LL; // max long long
Integer overflow: if you exceed the maximum value of a signed integer type, the result is undefined behavior — the compiler can do anything. For unsigned types, overflow wraps around modulo 2n (well-defined). For very large integers, use long long or a library.
Floating-point types
Floating-point types store numbers with a fractional part by encoding them in scientific notation internally. C++ provides three: float (single precision, 32 bits), double (double precision, 64 bits), and long double (extended precision, 80 or 128 bits depending on the platform). In almost all general-purpose code, double is the right choice. float is used when memory is tight (graphics, embedded) and long double when extreme precision is needed (scientific computation).
double price = 9.99; double pi = 3.14159265358979; float smallValue = 1.5f; // 'f' suffix makes it a float literal long double precise = 3.14159265358979323846L; // 'L' suffix // Scientific notation — both are the same: double avogadro = 6.022e23; // 6.022 × 10²³ double electron = 9.109e-31; // 9.109 × 10⁻³¹
Floating-point precision: floating-point numbers cannot represent most decimal fractions exactly. The value 0.1 does not have an exact binary representation, so it is stored as the nearest representable value. This causes surprising comparison results:
double x = 0.1 + 0.2;
// x is NOT exactly 0.3 — it is 0.30000000000000004...
// Never test floating-point equality directly:
if (x == 0.3) { ... } // may never be true!
// Instead, test whether the difference is smaller than some tolerance:
const double EPSILON = 1e-9;
if (std::abs(x - 0.3) < EPSILON) { ... } // correct approachInteger division: when both operands are integers, division produces an integer result — the fractional part is discarded, not rounded. This is a common source of bugs when computing averages or ratios. To get a decimal result, make at least one operand a double.
int a = 7, b = 2; int result = a / b; // result = 3 (fractional part dropped!) double ratio = a / b; // ratio = 3.0 — STILL integer division! double ratio2 = (double)a / b; // ratio2 = 3.5 — correct: cast first double ratio3 = a / 2.0; // ratio3 = 3.5 — 2.0 is a double literal
bool — true and false
The bool type holds exactly two values: true and false. It is used for conditions, flags, and the return type of predicate functions. Internally, C++ represents true as 1 and false as 0 — so you can mix booleans and integers in arithmetic, but it is usually a code smell to do so.
bool isOpen = true;
bool isEmpty = false;
// Comparison operators produce bool:
int x = 5;
bool isPositive = (x > 0); // true
bool isEven = (x % 2 == 0); // false
// Logical operators combine bools:
bool both = isPositive && isEven; // false (AND)
bool either = isPositive || isEven; // true (OR)
bool neither = !isPositive; // false (NOT)
// Any non-zero integer is truthy:
if (42) { } // executes — 42 is non-zero
if (0) { } // does not execute — 0 is falsechar — individual characters
The chartype stores a single character. It occupies 8 bits and its value is the character's ASCII code — an integer from 0 to 127 that maps to letters, digits, punctuation, and control characters. Because char is just a small integer, you can do arithmetic on it: adding 1 to 'a' gives 'b', and subtracting '0' from a digit character converts it to the corresponding integer.
char grade = 'A'; // single quotes for char literals char newline = '\n'; // escape sequences work in char literals char digit = '7'; // char is a small integer — arithmetic is valid: char next = grade + 1; // 'B' int code = grade; // 65 — the ASCII code of 'A' // Converting a digit character to its integer value: char c = '5'; int n = c - '0'; // n = 5 (works because '0'...'9' are consecutive) // Printing a char vs its integer value: std::cout << grade; // prints: A std::cout << (int)grade; // prints: 65
For text beyond ASCII (Unicode), C++ provides wchar_t, char16_t, and char32_t, and the standard library's std::string stores sequences of char that are often UTF-8 encoded. For the beginner path, plain char and std::string are sufficient.
Checking sizes with sizeof
The sizeof operator tells you how many bytes a type or variable occupies on your platform. This is useful for understanding memory layout and for writing portable code. The result has type std::size_t (an unsigned integer type).
#include <iostream>
int main()
{
std::cout << "char: " << sizeof(char) << " byte\n"; // always 1
std::cout << "int: " << sizeof(int) << " bytes\n"; // usually 4
std::cout << "long long: " << sizeof(long long) << " bytes\n"; // usually 8
std::cout << "double: " << sizeof(double) << " bytes\n"; // usually 8
std::cout << "bool: " << sizeof(bool) << " byte\n"; // usually 1
}Key rules to remember
Use int for most whole-number arithmetic, double for most fractional arithmetic
These are the types most library functions expect. Switch to long long when values might exceed 2 billion, and to float only when you specifically need to save memory.
Integer division drops the fractional part — cast before dividing if you need a decimal result
7 / 2 is 3, not 3.5. To get 3.5, write (double)7 / 2 or 7 / 2.0. This is one of the most common beginner bugs.
Never compare floating-point values with ==
Use std::abs(a - b) < epsilon for an equality test. The threshold epsilon depends on the scale of your values.
Signed integer overflow is undefined behavior
Do not let an int exceed INT_MAX (~2.1 billion). The compiler can assume overflow never happens and generate incorrect code if it does.
char is a small integer — arithmetic on characters is valid and useful
Subtracting '0' from a digit character converts it to its integer value. Adding 32 to an uppercase letter converts it to lowercase (for ASCII letters).