Інформатика · Математика · Програмування
📅 Квітень 2026 ⏱ ≈ 12 хв читання 🎯 Початковий–середній рівень · Останнє оновлення: 28 травня 2026 р.

Двійкові числа та IEEE 754 — як комп'ютери представляють числа

Кожне число, з яким працює комп'ютер, зрештою зберігається як послідовність двійкових цифр. Розуміння системи числення з основою 2, доповняльного коду для від'ємних цілих і стандарту IEEE 754 — фундамент програмування й розуміння того, чому 0.1 + 0.2 ≠ 0.3.

1. Основи двійкової та шістнадцяткової систем

Позиційні системи числення: Основа 10 (десяткова): 1027 = 1×10³ + 0×10² + 2×10¹ + 7×10⁰ Основа 2 (двійкова): 1101 = 1×2³ + 1×2² + 0×2¹ + 1×2⁰ = 8+4+0+1 = 13 Основа 16 (шістн.): 1A2F = 1×16³ + 10×16² + 2×16¹ + 15×16⁰ = 6703 Десяткова → двійкова (метод ділення на 2): 13 ÷ 2 = 6 ост 1 6 ÷ 2 = 3 ост 0 → читаємо остачі знизу вгору: 1101 3 ÷ 2 = 1 ост 1 1 ÷ 2 = 0 ост 1 Шістн. система використовує цифри 0–9, A–F (10–15). Кожна шістн. цифра представляє рівно 4 біти: 0x1A2F = 0001 1010 0010 1111 Беззнакові 8-бітні цілі: від 0 до 255 (2⁸ - 1) Беззнакові 16-бітні: від 0 до 65535 Беззнакові 32-бітні: від 0 до 4 294 967 295 Беззнакові 64-бітні: від 0 до 18 446 744 073 709 551 615

2. Доповняльний код

Домінувальне кодування знакових цілих. Для n-бітного числа старший біт має вагу −2^(n-1) замість +2^(n-1):

Діапазон 8-бітного доповняльного коду: від -128 до +127 Приклади: 0000 0001 = +1 0111 1111 = +127 1000 0000 = -128 (єдине від'ємне значення без додатного відповідника!) 1111 1111 = -1 Перетворення додатного цілого n на -n: Метод: інвертувати всі біти, потім додати 1. Приклад: +5 = 0000 0101 інверсія: 1111 1010 +1: 1111 1011 = -5 ✓ Перевірка: 5 + (-5) = 0000 0101 + 1111 1011 = (1)0000 0000 ✓ (перенесення відкидається) Чому доповняльний код: • Схема додавання/віднімання однакова для знакових і беззнакових • Нуль має лише одне представлення (на відміну від прямого коду: +0 і -0) • Порівняння з нулем: достатньо перевірити знаковий біт Переповнення в n-бітній арифметиці: (+127) + 1 = 1000 0000 = -128 (циклічне перенесення — невизначена поведінка в C!) У більшості мов арифметика за модулем 2ⁿ (Java/JS завжди; C/C++ беззнакові завжди)

3. Побітові операції

AND (&): 0b1100 & 0b1010 = 0b1000 (обидва біти мають бути 1) OR (|): 0b1100 | 0b1010 = 0b1110 (хоча б один біт є 1) XOR (^): 0b1100 ^ 0b1010 = 0b0110 (рівно один біт є 1) NOT (~): ~0b1100 = 0b0011 (інвертувати всі біти) Зсув ліворуч (<>n): x >> n = x / 2ⁿ (арифметичний, зберігає знак) Беззнаковий зсув праворуч (>>>n): (заповнення нулями, завжди невід'ємний) Поширені трюки маніпуляції бітами: ───────────────────────────────────────────────────── Перевірити біт k: (x >> k) & 1 Встановити біт k: x | (1 << k) Скинути біт k: x & ~(1 << k) Перемкнути біт k: x ^ (1 << k) Перевірка степеня 2: n && !(n & (n-1)) Найнижчий встановл. біт: x & (-x) [виділити LSB] Скинути найнижчий: x & (x-1) [у циклах popcount] Округлити до наст. степ.2: --n; n|=n>>1; n|=n>>2; n|=n>>4; n|=n>>8; n|=n>>16; ++n Обмін без temp: a^=b; b^=a; a^=b [трюк обміну через XOR] Абсолютне значення: mask = n>>31; (n^mask) - mask ───────────────────────────────────────────────────── Застосування: визначення розміру геш-таблиць (наступний степінь 2), розміри груп потоків GPU, стиснення, шифрування, швидка маніпуляція пікселями.

4. Рухома кома IEEE 754

Стандарт IEEE 754 (1985, переглянутий 2008) визначає, як дійсні числа зберігаються у двійковому вигляді. Два основні формати:

Одинарна точність (32 біти, float): біт 31: знак s (0=додатне, 1=від'ємне) біти 30–23: порядок e (8 бітів, зміщення на 127) біти 22–0: мантиса m (23 біти, неявна провідна 1) Значення (нормальне): (-1)^s × 1.m × 2^(e-127) Подвійна точність (64 біти, double / JS Number): біт 63: знак s біти 62–52: порядок e (11 бітів, зміщення на 1023) біти 51–0: мантиса m (52 біти, неявна провідна 1) Значення (нормальне): (-1)^s × 1.m × 2^(e-1023) Приклад: 0.1 у double: s = 0 1.0001100110011... × 2^(-4) [1/10 не є скінченним у системі з основою 2!] Зберігається як: 0 01111111011 1001100110011001100110011001100110011001100110011010 Фактично збережене значення: 0.1000000000000000055511151231257827021181583404541015625 Саме тому: 0.1 + 0.2 = 0.30000000000000004 у більшості мов. Діапазон і точність подвійної точності: Найменше додатне нормальне: ~2.225 × 10⁻³⁰⁸ Найбільше нормальне: ~1.798 × 10³⁰⁸ Точність: ~15–17 значущих десяткових цифр "Машинний епсилон": ε = 2⁻⁵² ≈ 2.22 × 10⁻¹⁶ (1 ULP при величині 1)

5. Спеціальні значення: NaN, Inf, денормалі

Спеціальні бітові комбінації в IEEE 754: Порядок = усі 1 (255 для float, 2047 для double): мантиса = 0: ±нескінченність (напр., 1.0/0.0 = +Inf) мантиса ≠ 0: NaN (Not a Number, не число) (напр., 0.0/0.0, sqrt(-1.0)) Порядок = усі 0: мантиса = 0: ±нуль (існують і +0, і -0; +0 == -0, але 1/+0 ≠ 1/-0) мантиса ≠ 0: денормаль (субнормаль) — розширює діапазон поблизу нуля Значення: (-1)^s × 0.m × 2^(-1022) [без неявної провідної 1] Жертвує швидкістю заради поступового зникнення порядку Властивості NaN (IEEE 754): • NaN ≠ NaN (єдине значення, що не дорівнює саме собі) • Перевірка: isNaN(x) у JS / std::isnan(x) у C++ • Будь-яка операція з NaN дає NaN (NaN "поширюється") • NaN не впорядкований: ні NaN < x, ні NaN >= x не є істинними Арифметика нескінченності: +Inf + скінченне = +Inf ✓ +Inf - +Inf = NaN (невизначеність ∞ - ∞) +Inf × 0 = NaN (невизначеність 0 × ∞) скінченне / +Inf = ±0 ✓ Денормалі: важливі для числової стабільності поблизу нуля. На деякому обладнанні операції з денормалями у 100× повільніші (режим flush-to-zero).

6. Точність і округлення

Не всі цілі можна точно представити в подвійній точності! Double має 53 значущі біти → точні цілі до 2⁵³ = 9 007 199 254 740 992 Проблема: 9007199254740993 + 1 = 9007199254740992 (хибно — втрачено +1) Катастрофічне скорочення: x = 1234567890123456.5 y = 1234567890123456.0 x - y = ? → мало б бути 0.5, але обидва округлено до одного double → 0.0 ! Компенсоване підсумовування Кагана: Для підсумовування багатьох float, відстежуючи "втрачену" частину c: float sum = 0, c = 0; для кожного x: float y = x - c; float t = sum + y; c = (t - sum) - y; sum = t; Похибка O(ε) замість O(n·ε) для наївного підсумовування. Режими округлення (IEEE 754): • До найближчого, нічию до парного (за замовчуванням, "банківське округлення") • До +∞ • До -∞ • До нуля (відкидання) Інтервальна арифметика використовує округлення до +∞ і -∞ для гарантованих меж.

7. Пастки чисел у JavaScript

JavaScript має єдиний числовий тип: IEEE 754 double. Це призводить до деякої несподіваної поведінки:

// Усі числа JavaScript — 64-бітні double за IEEE 754
0.1 + 0.2 === 0.3      // false  (0.30000000000000004)
Number.MAX_SAFE_INTEGER  // 9007199254740991 (2^53 - 1)
Number.EPSILON           // 2.220446049250313e-16
Number.MAX_VALUE         // 1.7976931348623157e+308
Number.MIN_VALUE         // 5e-324 (найменше додатне, денормаль)

// Перевірка безпечного цілого
Number.isSafeInteger(9007199254740991)  // true
Number.isSafeInteger(9007199254740992)  // false!

// Порівняння чисел з рухомою комою
function approxEqual(a, b, eps = Number.EPSILON * 4) {
  return Math.abs(a - b) <= eps * Math.max(Math.abs(a), Math.abs(b), 1);
}

// BigInt для цілих довільної точності (ES2020+)
const big = 9007199254740993n;  // точно!
big + 1n === 9007199254740994n;  // true ✓

// Побітові операції перетворюють на int32, а потім назад на double!
2147483648 | 0   // -2147483648  (2^31 переходить у від'ємний int32)
0xFFFFFFFF | 0  // -1           (те саме)
0xFFFFFFFF >>> 0 // 4294967295   (беззнаковий -- використовуйте >>> для uint32)
Використовуйте BigInt для великих точних цілих і decimal.js / bignumber.js для точної десяткової арифметики у фінансових обчисленнях. Ніколи не порівнюйте числа з рухомою комою на точну рівність — завжди застосовуйте допуск, пропорційний до величини.
💻 Дослідити алгоритми →