Арифметика
Порядок операций
Как математики договорились о порядке
До XVI века математики писали формулы словами: «умножить три на сумму двух и пяти». Скобки ввёл Рафаэль Бомбелли в 1572 году - тот самый, кто первым не испугался корней из отрицательных чисел. Современную нотацию с приоритетами закрепил Готфрид Лейбниц к концу XVII века.
Единое соглашение о порядке операций позволило учёным разных стран читать формулы без перевода. Это стандартизация - тот же принцип, по которому сегодня NumPy, PyTorch и TensorFlow дают одинаковые ответы на `2 ** 3 ** 2`.
Вирусный пример: 6 ÷ 2(1+2). 50 миллионов просмотров. Casio говорит 1. Wolfram Alpha - 9. Оба правы. Это не вопрос математики - это вопрос конвенции. Та же конвенция, нарушенная в Python-коде, даёт `2**3**2 = 512` вместо ожидаемых 64 - и gradient flow нейросети сломан молча.
- **Python ML-баги**: `c * A @ B` vs `c * (A @ B)` - разные результаты, оба компилируются без ошибок
- **IEEE 754**: `(0.1 + 0.2) + 0.3 != 0.1 + (0.2 + 0.3)` - порядок скобок меняет floating point результат
- **CUDA воспроизводимость**: параллельный reduction sum нарушает ассоциативность, разные запуски дают разные числа в последних знаках
PEMDAS: иерархия вычислений
**6 ÷ 2(1+2).** В 2011 году этот пример набрал 50 миллионов просмотров на YouTube - и у него два «правильных» ответа: 1 и 9. Калькулятор Casio даёт 1. Wolfram Alpha - 9. Оба правы относительно своей конвенции. Порядок операций - это не закон природы. Это соглашение, которое все приняли для одного: чтобы записывать формулы без лишних скобок.
**PEMDAS (он же BODMAS)** - иерархия приоритетов: 1. **P** - Parentheses (скобки) 2. **E** - Exponents (степени) 3. **MD** - Multiplication / Division (одинаковый уровень, слева направо) 4. **AS** - Addition / Subtraction (одинаковый уровень, слева направо) Одинаковый приоритет = слева направо. Исключение: степени - правоассоциативны.
`2 ** 3 ** 2` - это тихая ловушка. Если написать loss-функцию с двойным возведением в степень и рассчитывать на `(2**3)**2 = 64` - получится `512`. Gradient flow сломается, и обучение пойдёт не туда. Stack Overflow содержит более 50 000 вопросов, связанных с приоритетом операторов в Python.
**Правоассоциативность степеней**: `a**b**c` = `a**(b**c)`, не `(a**b)**c`. Это математически стандартно: $2^{3^2}$ = $2^9$ = 512. При любом сомнении - скобки. Явное лучше неявного.
Чему равно 10 - 3 * 2 + 1?
Ассоциативность и плавающая точка
В математике $a + b + c = (a + b) + c = a + (b + c)$ - ассоциативный закон. В компьютере этого нет. **IEEE 754 floating point**: `(0.1 + 0.2) + 0.3 ≠ 0.1 + (0.2 + 0.3)` в Python. Порядок скобок меняет результат из-за ошибок округления. В CUDA reduction sum - параллельное сложение с непредсказуемым порядком - даёт разные результаты при каждом запуске.
**Коммутативность**: $a + b = b + a$ и $a \times b = b \times a$ - всегда верно (и в целых, и в floating point). **Ассоциативность**: $(a + b) + c = a + (b + c)$ - в целых верно, в floating point - нет. Именно поэтому `-ffast-math` в компиляторах нарушает математическую корректность: он разрешает перегруппировку слагаемых ради скорости.
**Вычитание и деление не коммутативны**: $a - b \neq b - a$, $a \div b \neq b \div a$. И не ассоциативны: $(a - b) - c \neq a - (b - c)$. Пример: $(10 - 3) - 2 = 5$, но $10 - (3 - 2) = 9$. Поэтому порядок слева направо для одинаковых приоритетов - не произвол, а необходимость.
Чему равно (10 - 3) - 2 vs 10 - (3 - 2)?
Таблица приоритетов Python и ML-ловушки
Python имеет 18 уровней приоритета. Большинство совпадают с математическим PEMDAS, но есть сюрпризы. `@` - оператор матричного умножения (PEP 465, 2015) - стоит на уровне `*` и `/`. `**` - правоассоциативен. Побитовые операторы `<<`, `>>`, `&`, `|` - между сравнениями и арифметикой, что ловит всех без исключения.
| Уровень | Операторы | Ассоциативность | ML-контекст |
|---|---|---|---|
| Высший | `**` | Правая | Степенной decay, weight initialization |
| Высокий | `*`, `/`, `//`, `%`, `@` | Левая | Матричные операции (@ = matmul) |
| Средний | `+`, `-` | Левая | Сложение лоссов, residual connections |
| Низкий | `<<`, `>>`, `&`, `|` | Левая | Битовые маски в CUDA, quantization |
| Низший | `not`, `and`, `or` | Левая | Логика условий в training loops |
Чему равно выражение `2 ** 2 ** 3` в Python?
Реальные баги от неправильного порядка
**TensorFlow и PyTorch**: `a @ b + c * d` вычисляется как `(a @ b) + (c * d)` - правильно. Но `a @ b * c` = `(a @ b) * c` - поэлементное умножение на скаляр, а не скалярное умножение перед матричным. В backward pass это меняет градиенты. Такие баги не бросают исключений - они молча дают неправильные веса.
Если код запускается без ошибок, порядок операций правильный
Неправильный порядок операций - тихий баг: код работает, результат неверный
`weight * pred - target ** 2` и `weight * (pred - target) ** 2` - оба валидный Python. Разница в loss-функции сделает градиенты неправильными, модель будет обучаться, но сходиться к другому минимуму.
В PyTorch: чем отличается `c * A @ B` от `c * (A @ B)`?
Ключевые идеи
- **PEMDAS**: скобки > степени > умножение/деление > сложение/вычитание. Одинаковый приоритет - слева направо
- **Степени правоассоциативны**: `2**3**2 = 2**(3**2) = 512`, не 64. Это тихий баг в ML-коде
- **Floating point нарушает ассоциативность**: `(a+b)+c ≠ a+(b+c)` - воспроизводимость GPU-экспериментов зависит от порядка
- **`@` оператор Python**: тот же приоритет что `*`, работает слева направо - критично для матричных цепочек
Связанные темы
Порядок операций - фундамент для всего, что считает машина:
- Степени — Правоассоциативность - высший приоритет в PEMDAS
- Компьютерная арифметика — Полная таблица приоритетов операторов Python/C/Rust
- Последовательности — Рекуррентности и формулы итерации требуют точного порядка операций
Вопросы для размышления
- Почему именно умножение имеет приоритет над сложением, а не наоборот? Что изменилось бы в записи полиномов $ax^2 + bx + c$?
- Floating point нарушает ассоциативность. Значит ли это, что PEMDAS неприменим к числам с плавающей точкой?
- В выражении `loss = weight * (pred - target) ** 2` - какие скобки обязательны, а какие можно убрать без изменения смысла?
Связанные уроки
- ar-18-powers-natural — Степени - второй приоритет и правоассоциативность: понять до изучения PEMDAS
- ar-45-computer-arithmetic — Приоритет операторов в Python/C: прямое продолжение PEMDAS на код
- calc-01-sequences — Последовательности и формулы рекуррентности требуют точного порядка операций
- calc-03-limits-intro