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

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

Флаги -Wall -Wextra — это твоя бесплатная бабушка, которая ворчит на каждую подозрительную строчку. Слушай бабушку.
Если ты пишешь на C
Почти все функции из этой книги есть и в чистом C: они живут в <math.h>. В конце каждой главы есть раздел «На чистом 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.
Pulsuz fraqment bitdi.