Skip to content
C++

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.

TypeTypical sizeRangeUse for
short16 bits−32 768 … 32 767Tight memory budgets
int32 bits−2 147 483 648 … 2 147 483 647Default whole-number arithmetic
long32 or 64 bits (platform)At least as wide as intSlightly wider integers
long long64 bits−9.2 × 10¹⁸ … 9.2 × 10¹⁸Very large integers (file sizes, IDs)
unsigned int32 bits0 … 4 294 967 295Counts, indices (never negative)
std::size_t64 bits on 64-bit systems0 … 2⁶⁴−1Sizes 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 approach

Integer 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 false

char — 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).

Sign in to track progress