Арифметика

Порядок операций

Как математики договорились о порядке

До 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
Порядок операций

0

1

Войти