From b01fd0047e8472c99c8a20966254c12663289e1a Mon Sep 17 00:00:00 2001 From: Nikolay Gagarinov Date: Tue, 23 Jun 2026 23:34:25 +0500 Subject: [PATCH] =?UTF-8?q?feat(20-arithmetics):=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=D1=81=20=D1=82=D0=B5=D0=BE=D1=80=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B8=D0=B7=20Python=20=D0=B8=20=D0=B0=D0=B4=D0=B0=D0=BF?= =?UTF-8?q?=D1=82=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=B4=20java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Расширена теория шести уроков модуля "Арифметика" до паритета с эталонным курсом Python и адаптирована под Java. Урок 80-linting в этот PR не входит: он перерабатывается отдельно в рамках миграции линтера на spotless. - 10-basics: целочисленное деление, остаток, оформление выражений - 20-operators: операнды, бинарные и унарные операции, унарный плюс и минус - 30-commutative: коммутативность, 2/8=0 как иллюстрация целочисл. деления - 40-composition: пошаговые вычисления, приоритет, унарный минус - 50-priority: приоритет операций, скобки, читаемость - 60-float: IEEE 754, целочисл. деление vs дробное, погрешность Исправлен баг: пример "3 + 4;" как самостоятельная программа не компилируется в Java (not a statement). Заменен на арифметику внутри println. Адаптация под Java: убраны ** и //, целочисленное деление вместо авто-float. data.yml: перенесены definitions/tips из Python, имена уроков оставлены Java. Задания (App.java/Test.java/EXERCISE.md) не менялись. Локаль только RU. Co-Authored-By: Claude Opus 4.8 (1M context) --- modules/20-arithmetics/10-basics/ru/README.md | 109 +++++++++++++++--- modules/20-arithmetics/10-basics/ru/data.yml | 12 +- .../20-arithmetics/20-operators/ru/README.md | 56 ++++++--- .../20-arithmetics/20-operators/ru/data.yml | 4 + .../30-commutative/ru/README.md | 43 +++++-- .../20-arithmetics/30-commutative/ru/data.yml | 9 +- .../40-composition/ru/README.md | 57 +++++++-- .../20-arithmetics/40-composition/ru/data.yml | 3 + .../20-arithmetics/50-priority/ru/README.md | 54 ++++++--- .../20-arithmetics/50-priority/ru/data.yml | 5 + modules/20-arithmetics/60-float/ru/README.md | 55 ++++++--- modules/20-arithmetics/60-float/ru/data.yml | 7 +- 12 files changed, 325 insertions(+), 89 deletions(-) diff --git a/modules/20-arithmetics/10-basics/ru/README.md b/modules/20-arithmetics/10-basics/ru/README.md index 4b55fdc..f63347a 100644 --- a/modules/20-arithmetics/10-basics/ru/README.md +++ b/modules/20-arithmetics/10-basics/ru/README.md @@ -1,23 +1,31 @@ -На базовом уровне компьютеры оперируют только числами. Даже в прикладных программах на высокоуровневых языках внутри много чисел и операций над ними. +На базовом уровне компьютеры работают только с числами. Даже когда вы пишете большое приложение на современном языке, внутри постоянно идут вычисления. Программа складывает, вычитает, умножает и делит числа тысячи раз в секунду. -К счастью, для старта достаточно знать обычную арифметику — с нее и начнем. +Сумма товаров в корзине интернет-магазина, координаты персонажа в игре, длительность видео в секундах. За всем этим стоят обычные арифметические операции. -Для сложения двух чисел в математике мы пишем, например, *3 + 4*. В программировании — то же самое. Вот программа, складывающая два числа: +К счастью, для старта хватит обычной школьной арифметики. С нее и начнем. + +## Сложение + +В математике сумму записывают как 3 + 4. В Java выражение выглядит так же. Само по себе оно ничего не показывает на экране, поэтому результат нужно вывести. + +Для вывода используют знакомую команду `System.out.println()`. Вот целая программа, которая складывает два числа и печатает результат: ```java class App { public static void main(String[] args) { - 3 + 4; + System.out.println(3 + 4); } } ``` -Если запустить эту программу на выполнение, то она тихо отработает и завершится. На экран ничего не будет выведено. Операция сложения, как и остальные операции, сама по себе ничего не делает, кроме сложения. +Сначала вычисляется сумма, затем готовое число попадает внутрь команды печати. -Чтобы воспользоваться результатом сложения, его нужно вывести на экран: - -```java +```text System.out.println(3 + 4); + └─┬─┘ + 7 + +System.out.println(7); → 7 ``` После запуска на экране появится результат: @@ -26,21 +34,84 @@ System.out.println(3 + 4); 7 ``` -Кроме сложения доступны следующие операции: +Если то же выражение взять в кавычки, результат изменится. В кавычках получается текст, и на экран выйдет строка как есть: + +```java +System.out.println("3 + 4"); // 3 + 4 +System.out.println(3 + 4); // 7 +``` + +В первой строке Java видит текст и печатает его буквально. Во второй видит арифметику и считает. -* `*` — умножение -* `/` — деление -* `-` — вычитание -* `%` — [остаток от деления](https://ru.wikipedia.org/wiki/Деление_с_остатком) +## Другие операции -Теперь давайте выведем на экран результат деления, а потом результат возведения в степень: +Кроме сложения в Java есть весь привычный набор операций: + +| Операция | Символ | Пример | Результат | +|--------------------|--------|---------|-----------| +| Сложение | `+` | `2 + 3` | `5` | +| Вычитание | `-` | `7 - 2` | `5` | +| Умножение | `*` | `4 * 3` | `12` | +| Деление | `/` | `8 / 2` | `4` | +| Остаток от деления | `%` | `7 % 3` | `1` | + +Выведем результат деления, а потом результат умножения: ```java -System.out.println(8 / 2); -System.out.println(3 * 3 * 3); +System.out.println(8 / 2); // 4 +System.out.println(3 * 3 * 3); // 27 ``` -```text -4 -27 +Отдельного оператора для возведения в степень в Java нет. Когда степень небольшая, число умножают само на себя нужное количество раз, как в примере выше с `3 * 3 * 3`. + +## Деление целых чисел + +С делением в Java связана важная особенность. Когда оба числа целые, результат тоже получается целым, а дробная часть отбрасывается: + +```java +System.out.println(8 / 2); // 4 +System.out.println(7 / 2); // 3 ``` + +Во втором примере на калькуляторе вышло бы 3.5. Java делит 7 на 2 нацело и оставляет только целую часть, то есть 3. Остаток теряется. + +Так происходит потому, что целые числа в Java хранятся отдельно от дробных. Для работы с дробями в Java есть особый вид чисел. Их называют числами с плавающей точкой и записывают через точку, например `3.5`. Пока нам хватит целых. + +## Остаток от деления + +Операция `%` достает то, что остается после деления нацело. + +```java +System.out.println(7 % 3); // 1 +``` + +Почему получается 1? Тройка помещается в семерке два раза, это дает 6. До семерки не хватает единицы, она и есть остаток. + +Еще примеры: + +```java +System.out.println(10 % 4); // 2, четверка вошла в десятку дважды, до 10 осталось 2 +System.out.println(15 % 5); // 0, пятерка делит 15 нацело +``` + +Остаток выручает во многих задачах. По нему проверяют, делится ли число нацело. Если остаток равен нулю, значит делится. Так отличают четные числа от нечетных, ведь остаток от деления на 2 равен 0 у четных и 1 у нечетных. А еще остаток помогает считать по кругу. Остаток от деления на 12 работает как циферблат часов, и `13 % 12` дает `1`, ведь после двенадцати отсчет начинается заново. Эта операция встречается в коде постоянно. + +## Оформление выражений + +Для Java между `3+4` и `3 + 4` разницы нет. Программа поймет оба варианта одинаково и в обоих случаях сложит числа. Разница только в читаемости. В программировании принято отбивать арифметические операторы пробелами, потому что так выражение легче воспринимать: + +```java +3 + 4 +8 / 2 +7 % 3 +``` + +Вариант без пробелов тоже рабочий: + +```java +3+4 +8/2 +7%3 +``` + +Выглядит он плотно, и глазу труднее. Привыкайте сразу писать с пробелами вокруг операторов. diff --git a/modules/20-arithmetics/10-basics/ru/data.yml b/modules/20-arithmetics/10-basics/ru/data.yml index 01fa145..6dd5a1a 100644 --- a/modules/20-arithmetics/10-basics/ru/data.yml +++ b/modules/20-arithmetics/10-basics/ru/data.yml @@ -2,5 +2,13 @@ name: Арифметические операции tips: - >- - Деление на ноль приводит к ошибке. Чтобы его не допустить, нужно знать про - условные конструкции (о них вы узнаете в следующих уроках). + Отбивайте арифметические операторы пробелами от операндов, это хороший стиль + программирования. Так выражение читается удобнее. + - Деление на ноль приводит к ошибке. + - >- + [Деление с остатком](https://ru.wikipedia.org/wiki/Деление_с_остатком) +definitions: + - name: Инструкция + description: >- + наименьшая автономная часть языка программирования, команда или набор + команд. Программа обычно представляет собой последовательность инструкций. diff --git a/modules/20-arithmetics/20-operators/ru/README.md b/modules/20-arithmetics/20-operators/ru/README.md index 852752e..977c3d4 100644 --- a/modules/20-arithmetics/20-operators/ru/README.md +++ b/modules/20-arithmetics/20-operators/ru/README.md @@ -1,33 +1,57 @@ -Перед тем, как двигаться дальше, разберем базовую терминологию. Знак операции, такой как `+`, называют оператором. **Оператор** — просто символ, который выполняет операцию, например, сложение: +Сначала разберем базовую терминологию. Знак операции вроде `+` называют **оператором**. Оператор задает действие, например сложение: ```java -System.out.println(8 + 2); // => 10 +System.out.println(8 + 2); // 10 ``` -В этом примере `+` — это оператор, а числа *8* и *2* — это **операнды**. +Здесь `+` работает как оператор, а числа `8` и `2` называют **операндами**. Операндами называют значения, к которым оператор применяет действие. -В случае сложения у нас есть два операнда: - -* Один слева -* Другой справа от знака *+* +```text +операнд оператор операнд результат + 8 + 2 → 10 + 5 - 3 → 2 + 4 * 3 → 12 +``` -Операции, которые требуют наличия двух операндов, называются **бинарными**. Если пропустить хотя бы один операнд, то программа завершится с синтаксической ошибкой. Например: +У сложения два операнда. Один стоит слева от знака, другой справа. Операции с двумя операндами называют **бинарными**. Если пропустить хотя бы один операнд, программа не скомпилируется и выдаст синтаксическую ошибку: +```java +System.out.println(3 + ); // так писать нельзя ``` -`3 + ;` + +Бинарными операции бывают не всегда. Существуют еще унарные, с одним операндом, и тернарные, с тремя. + +## Унарный минус + +Один и тот же знак иногда означает разные операции. Посмотрите на минус: + +```java +System.out.println(-3); // -3 ``` -Операции бывают не только бинарными. Бывают еще: +Здесь минус стоит перед одним числом и работает как унарный оператор. Он берет число `3` и возвращает противоположное ему, то есть `-3`. -* Унарные — с одним операндом -* Тернарные — с тремя операндами +Когда минус стоит между двумя числами, это уже вычитание: -Причем операторы могут выглядеть одинаково, но обозначать разные операции: +```java +System.out.println(5 - 2); // 3 +System.out.println(10 - 7); // 3 +``` + +Разница особенно заметна на отрицательных числах: ```java -System.out.println(-3); // => -3 +System.out.println(5 - -2); // 7 ``` -Выше пример применения унарной операции к числу *3*. Оператор «минус» перед тройкой говорит интерпретатору — возьми число *3* и найди противоположное, то есть *-3*. +Слева идет вычитание `5 - (...)`, а справа унарный минус превращает `2` в отрицательное число. Получается `5 - (-2)`, и это дает `7`. Минус на минус дает плюс, ровно как в школе. + +Значение минуса зависит от соседей. Рядом с двумя числами это вычитание, перед одним числом это смена знака. Запись `-3` одновременно и само число, и оператор с операндом. У многих языков программирования такая же логика, так что вещь это привычная. + +Унарным бывает и плюс. Запись `+5` лишь подчеркивает, что число положительное, и значения не меняет: + +```java +System.out.println(+5); // 5 +``` -Это немного может сбить с толку, потому что *-3* — это одновременно и число само по себе, и оператор с операндом, но у языков программирования такая структура. +Остальные арифметические операторы `*`, `/` и `%` всегда бинарные, им нужны два операнда. Только плюс и минус умеют работать сразу в двух ролях, и как бинарные, и как унарные. diff --git a/modules/20-arithmetics/20-operators/ru/data.yml b/modules/20-arithmetics/20-operators/ru/data.yml index 6521cbd..0833ea0 100644 --- a/modules/20-arithmetics/20-operators/ru/data.yml +++ b/modules/20-arithmetics/20-operators/ru/data.yml @@ -1,5 +1,9 @@ --- name: Операторы +tips: + - >- + Отбивайте арифметические операторы пробелами от операндов, это хороший стиль + программирования. definitions: - name: Арифметическая операция description: сложение, вычитание, умножение и деление. diff --git a/modules/20-arithmetics/30-commutative/ru/README.md b/modules/20-arithmetics/30-commutative/ru/README.md index 9745211..34d53c6 100644 --- a/modules/20-arithmetics/30-commutative/ru/README.md +++ b/modules/20-arithmetics/30-commutative/ru/README.md @@ -1,15 +1,44 @@ -Мы все помним со школы: «от перемены мест слагаемых сумма не меняется». Это один из базовых и интуитивно понятных принципов арифметики — **коммутативный закон**. +Мы все помним со школы фразу "от перемены мест слагаемых сумма не меняется". Это один из базовых принципов арифметики, он называется **коммутативным законом**. -Бинарная операция считается коммутативной, если, вы получаете тот же самый результат, поменяв местами операнды. Очевидно, что сложение — коммутативная операция: +## Что такое коммутативность -```text -3 + 2 = 2 + 3 +Операцию называют коммутативной, когда порядок операндов не влияет на результат. Поменяли значения местами и получили тот же ответ. Сложение коммутативно: + +```java +System.out.println(3 + 2); // 5 +System.out.println(2 + 3); // 5 ``` -А вот вычитание — это не коммутативная операция: +Одинаковый результат подтверждает коммутативность. ```text -2 - 3 ≠ 3 - 2 +2 + 3 = 5 3 + 2 = 5 +└──────────┬─────────┘ + одинаковый результат + +2 - 3 = -1 3 - 2 = 1 +└──────────┬─────────┘ + разный результат +``` + +## Некоммутативные операции + +Не все операции так устроены. Вычитание меняет результат, когда операнды меняются местами: + +```java +System.out.println(2 - 3); // -1 +System.out.println(3 - 2); // 1 +``` + +Деление тоже некоммутативно, и в Java это видно особенно ярко: + +```java +System.out.println(8 / 2); // 4 +System.out.println(2 / 8); // 0 ``` -В программировании этот закон работает точно так же, как в арифметике. Более того, большинство операций не являются коммутативными. Отсюда вывод: всегда обращайте внимание на порядок того, с чем работаете. +В первом случае восьмерка делится на двойку и дает `4`. Во втором двойка делится на восьмерку. Целых восьмерок в двойке не помещается ни одной, поэтому при делении целых чисел получается `0`. + +## В программировании так же, как в арифметике + +Java следует тем же математическим правилам, что и школьная арифметика. А раз большинство операций некоммутативны, порядок операндов всегда важен. Обращайте на него внимание, особенно с незнакомой операцией, и проверяйте порядок на примере вместо догадок. diff --git a/modules/20-arithmetics/30-commutative/ru/data.yml b/modules/20-arithmetics/30-commutative/ru/data.yml index 669d02f..7698d63 100644 --- a/modules/20-arithmetics/30-commutative/ru/data.yml +++ b/modules/20-arithmetics/30-commutative/ru/data.yml @@ -1,8 +1,11 @@ --- name: Коммутативная операция +tips: + - >- + [Подробнее про коммутативность](https://ru.wikipedia.org/wiki/Коммутативность) definitions: - name: Коммутативность description: >- - свойство операции, когда изменения порядка операндов не влияет на - результат. Например, сложение — коммутативная операция: от перемены мест - слагаемых сумма не меняется. + свойство операции, при котором изменение порядка операндов не влияет на + результат. Например, сложение коммутативно: от перемены мест слагаемых + сумма не меняется. diff --git a/modules/20-arithmetics/40-composition/ru/README.md b/modules/20-arithmetics/40-composition/ru/README.md index 936592b..7b2209b 100644 --- a/modules/20-arithmetics/40-composition/ru/README.md +++ b/modules/20-arithmetics/40-composition/ru/README.md @@ -1,25 +1,60 @@ -А что, если понадобится вычислить такое выражение: *3 + 5 - 2*? Именно так мы и запишем: +Что делать, когда в одной строке нужно несколько операций сразу? Их записывают подряд, и Java обрабатывает выражение шаг за шагом по строгим правилам. + +Возьмем пример из одних умножений: ```java -System.out.println(3 + 5 - 2); // 3 + 5 - 2 => 8 - 2 => 6 +System.out.println(2 * 4 * 5 * 10); // 400 ``` -Обратите внимание, что компьютер производит арифметические вычисления в правильном порядке: сначала деление и умножение, потом сложение и вычитание. Иногда этот порядок нужно изменить — об этом немного далее. +Чтобы понять, как считает Java, разберем выражение по шагам: + +- сначала вычисляется `2 * 4`, остается `8 * 5 * 10` +- затем `8 * 5`, остается `40 * 10` +- последнее умножение дает `400` + +Так операции соединяются друг с другом, и из простых действий собираются все более сложные выражения. + +## А если операции разные -Или другой пример: +Пока операторы одинаковые, все идет слева направо. А что будет, если смешать умножение и сложение? ```java -System.out.println(2 * 4 * 5 * 10); // 2 * 4 * 5 * 10 => 8 * 5 * 10 => 40 * 10 => 400 +System.out.println(2 + 3 * 4); ``` -Как видно, операции можно соединять друг с другом и таким образом вычислять все более сложные составные выражения. Чтобы представить себе то, как происходят вычисления внутри интерпретатора, давайте разберем пример: +Получится `20` или `14`? Ответ `14`. ```text -2 * 4 * 5 * 10 +2 + 3 * 4 + └─┬─┘ +2 + 12 +└──┬───┘ + 14 +``` + +У операций есть приоритет, как и в математике. Умножение выполняется раньше сложения. Поэтому сначала считается `3 * 4`, и только потом прибавляется двойка. Порядок можно изменить скобками. В выражении `(2 + 3) * 4` сначала сложат `2 + 3`, а результат `5` умножат на `4`, и выйдет `20`. + +То же правило работает с вычитанием: + +```java +System.out.println(10 - 2 * 3); // 4 +``` + +Сначала умножение `2 * 3`, потом `10 - 6`, итог `4`. + +## Отрицательные числа внутри выражения + +Когда в выражении есть унарный минус, он применяется к своему числу раньше остальных операций: + +```java +System.out.println(4 + -2); // 2 +System.out.println(6 - -2); // 8 ``` -В этом примере: +Разберем второй пример. Сначала унарный минус превращает `2` в `-2`, дальше операция читается как `6 - (-2)`, и это дает `8`. То же самое, что и `6 + 2`. + +## Что нужно запомнить -1. Сначала вычисляем *2 * 4* и получаем выражение *8 * 5 * 10* -2. Затем умножаем *8 * 5*. В итоге имеем *40 * 10* -3. В конце концов происходит последнее умножение, и получается результат *400* +- выражение может состоять из нескольких операций +- Java вычисляет их по шагам, слева направо и с учетом приоритета +- скобки позволяют явно задать порядок вычислений diff --git a/modules/20-arithmetics/40-composition/ru/data.yml b/modules/20-arithmetics/40-composition/ru/data.yml index f721c13..7329468 100644 --- a/modules/20-arithmetics/40-composition/ru/data.yml +++ b/modules/20-arithmetics/40-composition/ru/data.yml @@ -1,2 +1,5 @@ --- name: Композиция операций +definitions: + - name: Композиция + description: метод объединения нескольких простых операций в одну сложную. diff --git a/modules/20-arithmetics/50-priority/ru/README.md b/modules/20-arithmetics/50-priority/ru/README.md index 78dce9b..c00221b 100644 --- a/modules/20-arithmetics/50-priority/ru/README.md +++ b/modules/20-arithmetics/50-priority/ru/README.md @@ -1,38 +1,64 @@ -Посмотрите внимательно на выражение *2 + 2 * 2* и посчитайте в уме ответ. Правильный ответ: *6*. Если у вас получилось *8*, то этот урок для вас. +Посмотрите на выражение `2 + 2 * 2` и посчитайте ответ в уме. Правильный ответ `6`. Если получилось `8`, этот урок для вас. -В школьной математике мы изучали понятие «приоритет операции». Приоритет определяет, в какой последовательности должны выполняться операции. +```java +System.out.println(2 + 2 * 2); // 6 +``` -Например, умножение и деление имеют больший приоритет, чем сложение и вычитание: +Результат равен `6`, а вовсе не `8`, из-за приоритета операций. Приоритет определяет, в каком порядке выполняются действия. Умножение и деление идут раньше сложения и вычитания: ```text -2 + 3 * 2 = 8 +Приоритет операций (от высокого к низкому): + + * / % умножение, деление, остаток + ↓ + + - сложение, вычитание ``` -Но нередко вычисления должны происходить в порядке, отличном от стандартного приоритета. В сложных ситуациях приоритет можно задавать круглыми скобками, точно так же, как в школе, например: +Поэтому в примере выше сначала считается `2 * 2`, и только потом к результату прибавляется двойка. -```text -(2 + 2) * 2 +Когда рядом стоят операции одного приоритета, они выполняются слева направо: + +```java +System.out.println(8 / 2 * 3); // 12, сначала 8 / 2 = 4, затем 4 * 3 = 12 +``` + +## Управление порядком действий + +Иногда вычисления должны идти не так, как диктует приоритет. Тогда порядок задают круглыми скобками, ровно как в школе: + +```java +System.out.println((2 + 2) * 2); // 8 ``` -Скобки можно ставить вокруг любой операции. Они могут вкладываться друг в друга сколько угодно раз. Вот пара примеров: +Здесь скобки заставляют сначала сложить `2 + 2`, а уже потом умножить на двойку. + +Скобки можно ставить вокруг любой части выражения и вкладывать друг в друга сколько угодно раз: ```java -System.out.println(3 * (4 - 2)); // => 6 -System.out.println(7 * 3 + (4 / 2) - (8 + (2 - 1))); // => 14 +System.out.println(3 * (4 - 2)); // 6 +System.out.println(7 * 3 + (4 / 2) - (8 + (2 - 1))); // 14 ``` -Иногда выражение сложно воспринимать визуально. Тогда можно сделать его понятнее, расставив скобки, хотя они и не повлияют на приоритет: +Во втором примере сначала считаются скобки. `4 / 2` дает `2`, а `8 + (2 - 1)` дает `9`. Остается `21 + 2 - 9`, и в итоге `14`. + +Запомните одно правило. Всегда закрывайте скобки. Незакрытая скобка приводит к ошибке, и про закрывающую забывают даже опытные программисты. + +> Пишите скобки сразу парой. Например, вводите `()`, а потом заполняете внутреннюю часть. Большинство редакторов кода (в том числе наш) сами добавляют закрывающую скобку, как только вы написали открывающую. + +## Скобки для читаемости + +Иногда выражение работает правильно, но выглядит запутанно. В таких случаях скобки добавляют для наглядности. На результат они не влияют, зато читать становится удобнее. Было: ```java -System.out.println(8 / 2 + 5 - -4 / 2); // => 11 +System.out.println(8 / 2 + 5 - -4 / 2); // 11 ``` Стало: ```java -System.out.println(((8 / 2) + 5) - (-4 / 2)); // => 11 +System.out.println(((8 / 2) + 5) - (-4 / 2)); // 11 ``` -Запомните: код пишется для людей, потому что код будут читать люди, а машины будут только исполнять его. Для машин нет «более» понятного или «менее» понятного кода, независимо от того, является ли код корректным или нет. +Код пишут и читают люди, а машина только исполняет его. Для машины нет более или менее понятного кода, ей достаточно, чтобы код был синтаксически правильным. Аккуратное выражение со скобками выручает человека, особенно при работе в команде и при разборе ошибок. diff --git a/modules/20-arithmetics/50-priority/ru/data.yml b/modules/20-arithmetics/50-priority/ru/data.yml index da15a1d..2ce04d0 100644 --- a/modules/20-arithmetics/50-priority/ru/data.yml +++ b/modules/20-arithmetics/50-priority/ru/data.yml @@ -1,2 +1,7 @@ --- name: Приоритет операций +definitions: + - name: Выражение + description: >- + последовательность действий над данными, которая приводит к результату, + пригодному для дальнейшего использования. diff --git a/modules/20-arithmetics/60-float/ru/README.md b/modules/20-arithmetics/60-float/ru/README.md index b86723f..2d82b1b 100644 --- a/modules/20-arithmetics/60-float/ru/README.md +++ b/modules/20-arithmetics/60-float/ru/README.md @@ -1,34 +1,59 @@ +В математике есть разные виды чисел. Например: -В математике существуют разные виды чисел, например: +- натуральные, это целые числа от 1 и больше +- рациональные, это числа с точкой, например 0.5, 1.75, 2.25 -* **Натуральные** — это целые числа от 1 и больше -* **Рациональные** — это числа с точкой, например, 0.5 +С точки зрения математики тут все понятно. А вот с точки зрения компьютера между этими видами чисел лежит пропасть. Попробуйте в уме сложить `0.2` и `0.1`. Кажется, что выйдет `0.3`. Вот что на это скажет Java: -С точки зрения устройства компьютеров, между этими видами чисел — пропасть. Попробуем сложить два рациональных числа: +```java +System.out.println(0.2 + 0.1); // 0.30000000000000004 +``` + +Вместо привычных `0.3` получается `0.30000000000000004`. ```text -0.2 + 0.1 = 0.3 +Ожидание: 0.1 + 0.2 → 0.3 +Реальность: 0.1 + 0.2 → 0.30000000000000004 + └── погрешность хранения ``` -А теперь посмотрим, что на это скажет Java: +Тот же результат выдадут JavaScript, C++ и почти все остальные языки. + +## Почему так происходит + +Причина в устройстве компьютера. Память конечна, а рациональных чисел бесконечно много. Между `0.1` и `0.2` помещается бесконечно много других чисел, и сохранить их все компьютер не может. Поэтому он приближает число, стараясь уместить его в доступные биты. + +С целыми числами этой беды нет, их ограничивают сверху. У самого большого целого числа в Java даже есть имя: ```java -0.2 + 0.1; // 0.30000000000000004 +System.out.println(Integer.MAX_VALUE); // 2147483647 ``` -Операция сложения двух рациональных чисел внезапно привела к неточному вычислению результата. Тот же самый результат выдадут и другие языки программирования. +За записью `Integer.MAX_VALUE` прячется крайнее значение, дальше которого обычное целое число в Java не уходит. -Такое поведение обуславливается ограничениями вычислительных мощностей. В отличие от чисел, объем памяти конечен — при этом бесконечное количество чисел требовало бы бесконечного количества памяти для своего хранения. +С рациональными числами такой фокус не проходит, ведь они не выстроены в ровную цепочку. Приближенные значения называют числами с плавающей точкой (floating point numbers). Хранение и вычисления с ними подчиняются строгому стандарту IEEE 754, на который опирается большинство языков. -С натуральными числами эта проблема решается простым ограничением по верхней границе. Есть некоторое максимальное число, которое можно ввести: +## Когда появляются такие числа + +Числа с плавающей точкой встречаются чаще, чем кажется. Вот два основных случая. + +В первом случае вы сами пишете число с точкой, например `0.1`, `2.5` или `3.14`. + +Во втором случае в делении участвует дробное число: ```java -System.out.println(Integer.MAX_VALUE); -// => 2147483647 +System.out.println(1.0 / 2); // 0.5 +System.out.println(2.0 / 3); // 0.6666666666666666 ``` -С рациональными числами такой финт не пройдет. Дело в том, что они не выстроены в непрерывную цепочку, между *0.1* и *0.2* лежит бесконечное множество чисел. +Тут стоит вспомнить про целочисленное деление. Когда оба операнда целые, Java делит нацело, и `1 / 2` даст `0`. Достаточно сделать хотя бы один операнд дробным, и результат становится числом с плавающей точкой. Даже когда результат выглядит красиво, внутри он все равно хранится приближенно. Некоторые дроби, например `1.0 / 3`, в двоичной системе вообще не записать точно. + +## Где это критично + +Обычно маленькая погрешность не мешает. Но в финансовых расчетах, инженерных задачах и при точном сравнении чисел она превращается в проблему. Ошибка в доли копейки портит итоговую сумму, а длинная цепочка вычислений постепенно накапливает неточность. + +В реальных программах с этим борются по-разному. Деньги часто хранят в минимальных единицах, например в копейках, то есть в целых числах. Результат округляют до нужного количества знаков. Числа сравнивают с небольшим допуском. Для точных вычислений берут специальные типы данных. -А как тогда хранить рациональные числа? Подавляющее число языков программирования в этом случае опирается на единый стандарт, который описывает как организовывать память в таких случаях. +## Что нужно запомнить -Разработчикам важно понимать, что операции с плавающими числами неточны, но эту точность можно регулировать. Это значит, что при решении задач с подобными числами необходимо прибегать к специальным трюкам, которые позволяют добиться необходимой точности. +Операции с числами с плавающей точкой не всегда точны, и это нормально. Так ведет себя большинство языков программирования, и причина в устройстве памяти. Точность можно контролировать округлением или сравнением с допуском. А для денег и научных расчетов лучше сразу брать специальные типы данных. diff --git a/modules/20-arithmetics/60-float/ru/data.yml b/modules/20-arithmetics/60-float/ru/data.yml index 32f20fc..c681ab9 100644 --- a/modules/20-arithmetics/60-float/ru/data.yml +++ b/modules/20-arithmetics/60-float/ru/data.yml @@ -1,6 +1,9 @@ --- name: Числа с плавающей точкой tips: - - > + - >- [Что нужно знать про арифметику с плавающей - запятой](https://habr.com/post/112953/) + запятой](https://habr.com/ru/articles/112953/) +definitions: + - name: Рациональное число + description: число, которое можно представить в виде обыкновенной дроби.