
Полная версия
С++/C и математика без соплей код

Алексей Примак
С++/C и математика без соплей код
C++ и математика без соплей
Все математические функции C++ и C: от классики до C++23, с примерами, шутками и задачами
Table of Contents
Предисловие: какого хрена мы тут собрались
Эта книга научит тебя всем математическим функциям стандартного C++ — от банального abs до гамма-функции, — и в конце каждой главы будут задачи, от которых не отвертишься.
Я буду шутить, иногда материться и разжёвывать так, чтобы дошло даже в четыре утра. Математика в программировании — это не страшно. Страшно, когда ты сравниваешь два double через == и потом три дня ищешь баг.
Что нужно
• Компилятор с поддержкой C++20: g++ 10+, clang 12+ или MSVC 2019+.
• Голова. Желательно своя.
• Терпение на задачи. Ответы в конце, но подглядывать — это как читать последнюю страницу детектива: можно, но ты лох.
Шаблон каждой программы
Почти все функции живут в заголовке

Компилируем:

Флаги -Wall -Wextra — это твоя бесплатная бабушка, которая ворчит на каждую подозрительную строчку. Слушай бабушку.
Если ты пишешь на C
Почти все функции из этой книги есть и в чистом C: они живут в

Флаг -lm подключает математическую библиотеку. Забудешь его — получишь ошибку линковки undefined reference to sqrt и полчаса недоумения. Флаг -std=c2x включает свежий стандарт C23 (в новых компиляторах можно писать -std=c23).
Что такое «современный C++»
Каждая глава заканчивается разделом «Современный C++»: там то же самое, но с фишками C++20 и C++23 — std::format, std::print, ranges, концепты, constexpr. Так сейчас пишут в нормальных командах. Для C++23 компилируй с флагом -std=c++23 (g++ 14+ или clang 18+).
Три правила, которые спасут тебе жопу
Всегда пиши std:: перед функцией. Без него abs(-2.5) может вызвать целочисленную версию из C и вернуть 2. Охренеть, да? Вот поэтому.
Тригонометрия работает в радианах, а не в градусах. Запомни это раз и навсегда.
double — это приближение. 0.1 + 0.2 != 0.3, и это не баг, это физика двоичных дробей.
Глава 1. Арифметика, или где тебя наебёт компьютер
Главный вывод главы: 5 / 2 в C++ равно 2, а остаток от деления отрицательного числа бывает отрицательным. Сейчас разберём почему.
1.1. Модуль: abs и fabs
Модуль — это «расстояние до нуля». Минус выкидывается, как бывший из жизни.

Подвох: std::abs(INT_MIN) — неопределённое поведение. У int минимум −2147483648, а максимум 2147483647. Положительной пары у минимума просто нет, и программа может сделать что угодно, хоть стихи напечатать.
1.2. Деление: целое против дробного
Если оба операнда целые — дробная часть отрезается нахрен. Не округляется, а именно отрезается к нулю.

Классическая ошибка новичка: double avg = sum / count;, где обе переменные int. Деление уже целое, а в double кладётся огрызок. Лечится приведением хотя бы одного операнда.
1.3. Остаток: %, fmod, remainder
Оператор % работает только с целыми. Знак результата совпадает со знаком делимого.

Для дробных чисел — std::fmod (знак как у делимого) и std::remainder (ближайшее к нулю, может быть отрицательным):

Хочешь «математический» остаток, всегда от 0 до m−1? Пиши так:

Это пригодится для дней недели, кругового буфера и всего, что крутится по кругу.
1.4. Переполнение
int не резиновый. Сложи два больших — получишь отрицательное число, а для знаковых типов это вообще неопределённое поведение.

Правило: если результат может быть большим — считай в long long с самого начала, а не после.
1.5. Современный C++: честные сравнения и проверка диапазона
Сравнение знакового и беззнакового числа — мина под ногами. Перед сравнением -1 молча превращается в 4294967295, и -1 < 1u оказывается ложью. C++20 даёт функции, которые сравнивают по-человечески:

Правило: если в сравнении встретились int и unsigned (например, i < v.size()), либо приводи типы явно, либо используй std::cmp_less.
1.6. На чистом C
В C нет std:: и нет перегрузки функций. Поэтому у модуля четыре разных имени: abs для int, labs для long, llabs для long long (все три в
Зато в C23 появился заголовок

Задачи к главе 1
Напиши программу, которая читает два целых числа и выводит их среднее арифметическое как дробное. Проверь на 3 и 4 — должно быть 3.5, а не 3.
Дано количество секунд (например, 100000). Выведи его в формате дни:часы:минуты:секунды, используя только / и %.
Сегодня среда (день 2, если понедельник — 0). Какой день недели будет через n дней и был n дней назад? Используй функцию mod из раздела 1.3, проверь на n = 100.
Почему std::abs(-2.7) без #include
Посчитай сумму чисел от 1 до 100000 в int и в long long. Сравни результаты и объясни разницу.
Глава 2. Степени и корни: pow, sqrt, cbrt, hypot
Главный вывод: pow — универсальный, но медленный и капризный; для квадрата пиши x * x, для корня — sqrt, для длины вектора — hypot.
2.1. pow — возведение в степень
В C++ нет оператора ^ для степени. 2 ^ 3 — это побитовое XOR, и оно равно 1. Сколько людей на этом погорело — не сосчитать.

Подвох номер два: pow возвращает double. (int)std::pow(10, 2) на некоторых старых компиляторах давал 99, потому что получалось 99.9999999. Для целых степеней пиши свою функцию:

Это называется быстрое возведение в степень. Выучи его — на собеседованиях спрашивают постоянно.
2.2. sqrt и cbrt — корни

Запомни: cbrt спокойно жрёт отрицательные числа, а pow(x, 1.0/3) — нет. Для кубического корня всегда cbrt.
Проверка «является ли число полным квадратом» — тоже ловушка из-за погрешности:

2.3. hypot — гипотенуза без переполнения
Теорема Пифагора в одну функцию:

Почему не sqrt(a*a + b*b)? Потому что при a = 1e200 квадрат улетает в бесконечность, а hypot считает аккуратно. Плюс читается красивее.
Пример: расстояние между двумя точками

2.4. Современный C++: степень на этапе компиляции
Функцию ipow можно сделать constexpr — тогда компилятор посчитает её ещё до запуска программы. А концепт std::integral не даст сунуть туда double:

static_assert — это тест, который выполняется при компиляции. Сломал функцию — программа даже не соберётся. В C++26 обещают сделать constexpr и большую часть
2.5. На чистом C
Без перегрузки у каждой функции три версии: sqrtf для float, sqrt для double, sqrtl для long double. Чтобы не помнить суффиксы, есть

Задачи к главе 2
Выведи 2 ^ 3 и std::pow(2, 3). Объясни, почему результаты разные.
Реализуй ipow и посчитай 2 в 62-й степени. Сравни с std::pow(2.0, 62) — совпадает ли до последней цифры?
Решение квадратного уравнения: по a, b, c выведи корни через дискриминант. Обработай три случая: два корня, один, ни одного.
Даны три точки на плоскости. Найди периметр треугольника с помощью hypot.
Выведи все полные квадраты от 1 до 1000 с помощью функции isSquare. Сколько их?
Глава 3. Логарифмы и экспонента: log, exp и их родня
Главный вывод: логарифм отвечает на вопрос «в какую степень возвести основание, чтобы получить число», а log в C++ — это натуральный логарифм, а не десятичный.
3.1. Что такое логарифм на пальцах
Если 2 в 10-й = 1024, то логарифм 1024 по основанию 2 равен 10. Всё. Логарифм — это «сколько раз надо умножить», вывернутое наизнанку.
На практике логарифм считает «сколько шагов». Сколько раз можно поделить 1000000 пополам? log2(1000000) ≈ 20. Вот почему бинарный поиск такой быстрый.
Конец ознакомительного фрагмента.
Текст предоставлен ООО «Литрес».
Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.
Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.




