$ sudo teach IT
Модуль 2 · Примитивные операции

Урок 2.4 — Работа с числами

Числа — основа вычислений. Разберём арифметику, класс Math, Random и подводные камни целочисленного деления.

Числа — основа программирования. Счёт в играх, цены в интернет-магазинах, координаты на карте, процент заряда батареи — всё это числа. В этом уроке мы научимся выполнять арифметические операции, округлять, брать корни и степени, а также генерировать случайные числа. Мы поднимемся на новый уровень — ты перестанешь просто складывать 2 + 2 и начнёшь писать настоящие вычислительные программы.

➕

Арифметические операторы

В C# есть пять базовых арифметических операторов. Они работают точно так же, как на уроках математики в школе. Таблица операторов:

Оператор Название Пример Результат
+Сложение10 + 313
-Вычитание10 - 37
*Умножение10 * 330
/Деление10 / 33 (int) или 3.333 (double)
%Остаток от деления10 % 31

Арифметика в C#:

Во всех примерах ниже показаны операции с разными типами и их результаты.

C# · Базовые арифметические операции
int a = 15;
int b = 4;

Console.WriteLine(a + b);   // 19 — сложение
Console.WriteLine(a - b);   // 11 — вычитание
Console.WriteLine(a * b);   // 60 — умножение
Console.WriteLine(a / b);   // 3  — целочисленное деление! (15 / 4 = 3, остаток 3)
Console.WriteLine(a % b);   // 3  — остаток от деления (15 = 4*3 + 3)

double x = 15.0;
double y = 4.0;

Console.WriteLine(x / y);   // 3.75 — дробное деление (один операнд double)

Операторы подчиняются приоритетам математики: умножение и деление выполняются раньше сложения и вычитания. Скобки () меняют порядок:

C# · Приоритет операций
int result1 = 2 + 3 * 4;        // 14 (сначала 3*4=12, потом 2+12=14)
int result2 = (2 + 3) * 4;      // 20 (сначала 2+3=5, потом 5*4=20)

int result3 = 10 - 4 + 2;       // 8 (слева направо: 10-4=6, 6+2=8)
int result4 = 10 - (4 + 2);     // 4 (сначала 4+2=6, потом 10-6=4)

Console.WriteLine(result1);      // 14
Console.WriteLine(result2);      // 20
Console.WriteLine(result3);      // 8
Console.WriteLine(result4);      // 4
💡 Правило из школы: сначала * / %, потом + -. Если сомневаешься — ставь скобки (). Код будет понятнее, а порядок — явным.
➗

Целочисленное деление и остаток %

Одна из главных ловушек для новичков в C# — целочисленное деление. Когда ты делишь int на int, результат всегда будет int. Дробная часть просто отбрасывается. Чтобы получить дробный результат, хотя бы один из операндов должен быть double.

⚠️ Классическая ошибка новичка

int a = 5;
int b = 2;
double result = a / b;    // 2.0, а не 2.5!
Console.WriteLine(result); // 2

// Почему? Потому что a / b сначала вычисляется как 5 / 2 = 2 (int),
// а потом 2 (int) преобразуется в 2.0 (double).

// Исправление:
double result2 = (double)a / b;  // 2.5 — явно преобразуем один операнд

Оператор % (остаток от деления) — невероятно полезная штука. Он возвращает остаток от деления одного числа на другое. В школе это называли «деление с остатком»:

C# · Оператор остатка %
Console.WriteLine(10 % 3);   // 1  (10 = 3*3 + 1)
Console.WriteLine(17 % 5);   // 2  (17 = 5*3 + 2)
Console.WriteLine(8 % 2);    // 0  (8 чётное — делится нацело)
Console.WriteLine(9 % 2);    // 1  (9 нечётное)
Console.WriteLine(100 % 7);  // 2  (100 = 7*14 + 2)
Console.WriteLine(0 % 5);    // 0  (0 делить на что угодно = 0)

Где применяется % в реальных программах:

  • Проверка чётности: число % 2 == 0 — чётное
  • Ограничение диапазона: index % max — никогда не выйдет за пределы max
  • Разбиение на группы: каждый N-ый элемент (например, каждый 3-ий)
  • Время: сколько минут осталось после перевода секунд в часы
  • Криптография: многие алгоритмы шифрования используют модульную арифметику

💡 Золотое правило: если тебе нужно поделить целые числа и получить дробный результат — преобразуй один из операндов в double. Если нужен только целый результат — дели спокойно, C# сделает это сам. Оператор % даст тебе остаток.

⬆️

Инкремент и декремент: ++ и --

В программировании постоянно нужно увеличить число на 1 или уменьшить на 1: счётчик очков, номер шага в цикле, индекс элемента. Вместо того чтобы писать score = score + 1, C# предлагает краткую запись:

Оператор Название Эквивалент
x++Постфиксный инкрементx = x + 1
++xПрефиксный инкрементx = x + 1
x--Постфиксный декрементx = x - 1
--xПрефиксный декрементx = x - 1

Разница между x++ и ++x проявляется, когда оператор используется внутри другого выражения. В обоих случаях переменная увеличивается. Но x++ возвращает старое значение (до увеличения), а ++x — новое (после увеличения).

C# · Префикс vs постфикс
int a = 5;
int b = 5;

Console.WriteLine(a++);   // 5 (сначала вернули a=5, потом увеличили до 6)
Console.WriteLine(a);     // 6 (теперь a=6)

Console.WriteLine(++b);   // 6 (сначала увеличили b до 6, потом вернули 6)
Console.WriteLine(b);     // 6 (b=6)

// Демонстрация разницы:
int x = 3;
int y = 3;
int result1 = x++ * 2;    // result1 = 6 (x=3, потом x стал 4)
int result2 = ++y * 2;    // result2 = 8 (y стал 4, потом 4*2=8)

Console.WriteLine($"result1={result1}, x={x}");      // result1=6, x=4
Console.WriteLine($"result2={result2}, y={y}");      // result2=8, y=4
💡 На начальном этапе используй x++ и --x отдельно, не в составе других выражений. Так ты избежишь путаницы. Разницу между префиксом и постфиксом мы подробно разберём в теме «Циклы».
⚡

Составные операторы присваивания

Очень часто нужно изменить переменную, применив к ней операцию: x = x + 5, price = price * 0.9. C# предоставляет составные операторы для таких случаев — это сокращения, которые совмещают операцию и присваивание:

Оператор Эквивалент Пример
+=x = x + yscore += 10;
-=x = x - yfuel -= 5;
*=x = x * yprice *= 0.9;
/=x = x / ycount /= 2;
%=x = x % yindex %= 3;
C# · Составные операторы
int score = 0;

score += 10;       // score = 0 + 10 = 10
score += 5;        // score = 10 + 5 = 15
Console.WriteLine(score);  // 15

score -= 3;        // score = 15 - 3 = 12
Console.WriteLine(score);  // 12

score *= 2;        // score = 12 * 2 = 24
Console.WriteLine(score);  // 24

score /= 4;        // score = 24 / 4 = 6
Console.WriteLine(score);  // 6

score %= 5;        // score = 6 % 5 = 1
Console.WriteLine(score);  // 1

Составные операторы не только сокращают код, но и читаются естественнее: «увеличь счёт на 10» вместо «счёт равен счёт плюс 10». Программисты используют их постоянно.

💡 Запомни: += работает и со строками! string message = "Привет"; message += ", мир!"; — это то же самое, что конкатенация. Но для чисел это гораздо полезнее.

📐

Класс Math — математика в C#

C# предоставляет встроенный класс Math, который содержит множество математических функций. Тебе не нужно писать их самому — просто вызывай готовые методы. Все методы класса Math статические, поэтому вызываются через имя класса: Math.MethodName().

Math.Abs() Модуль числа (абсолютное значение)

Возвращает абсолютное значение числа — расстояние от нуля. Всегда неотрицательное.

Console.WriteLine(Math.Abs(-5));      // 5
Console.WriteLine(Math.Abs(5));       // 5
Console.WriteLine(Math.Abs(-3.14));   // 3.14
Console.WriteLine(Math.Abs(0));       // 0
Math.Max() / Min() Максимум и минимум

Возвращает большее (Max) или меньшее (Min) из двух чисел. Принимает два аргумента одного типа.

int maxVal = Math.Max(10, 20);        // 20
int minVal = Math.Min(10, 20);        // 10

double maxD = Math.Max(3.14, 2.71);   // 3.14
double minD = Math.Min(3.14, 2.71);   // 2.71

// Можно вкладывать вызовы:
int maxOfThree = Math.Max(5, Math.Max(9, 3));  // 9
Math.Pow() Возведение в степень

Возводит число (первый аргумент) в указанную степень (второй аргумент). Результат всегда double.

Console.WriteLine(Math.Pow(2, 3));     // 8 (2 в кубе)
Console.WriteLine(Math.Pow(5, 2));     // 25 (5 в квадрате)
Console.WriteLine(Math.Pow(10, 0));    // 1 (любое число в степени 0 = 1)
Console.WriteLine(Math.Pow(16, 0.5));  // 4 (квадратный корень через степень 1/2)
Console.WriteLine(Math.Pow(2, 10));    // 1024 (2 в 10-й степени)
Math.Sqrt() Квадратный корень

Извлекает квадратный корень числа. Если число отрицательное — вернёт NaN (Not a Number).

Console.WriteLine(Math.Sqrt(25));      // 5
Console.WriteLine(Math.Sqrt(2));       // 1.41421356237
Console.WriteLine(Math.Sqrt(0.25));    // 0.5
Console.WriteLine(Math.Sqrt(-1));      // NaN (Not a Number)

// Используем для вычисления гипотенузы:
double a = 3;
double b = 4;
double c = Math.Sqrt(a * a + b * b);  // 5.0 (теорема Пифагора)
Round / Floor / Ceiling Округление чисел

Три способа округлить дробное число:

  • Round — математическое округление (0.5 → вверх, 0.4 → вниз)
  • Floor — всегда вниз (пол — пол внизу)
  • Ceiling — всегда вверх (потолок — потолок сверху)
double d = 3.75;

Console.WriteLine(Math.Round(d));        // 4 (3.75 → 4)
Console.WriteLine(Math.Floor(d));        // 3 (всегда вниз)
Console.WriteLine(Math.Ceiling(d));      // 4 (всегда вверх)

double e = 3.49;
Console.WriteLine(Math.Round(e));        // 3 (3.49 → 3)
Console.WriteLine(Math.Floor(e));        // 3
Console.WriteLine(Math.Ceiling(e));      // 4 (даже 3.0001 → 4)

// Округление до N знаков после запятой:
Console.WriteLine(Math.Round(3.14159, 2));   // 3.14
Console.WriteLine(Math.Round(3.14159, 4));   // 3.1416

Console.WriteLine(Math.Floor(3.99));    // 3
Console.WriteLine(Math.Ceiling(3.01));  // 4

// Отрицательные числа:
Console.WriteLine(Math.Floor(-1.5));    // -2 (вниз от -1.5 это -2!)
Console.WriteLine(Math.Ceiling(-1.5));  // -1 (вверх от -1.5 это -1)
💡 Math.Floor(-1.5) даёт -2, потому что «пол» всегда ниже. -2 < -1.5, значит это «пол». А -1 > -1.5, это «потолок». Логика, но новичков это удивляет.

Константы Math.PI и Math.E

Класс Math содержит две полезные константы:

C# · Математические константы
Console.WriteLine(Math.PI);   // 3.14159265358979
Console.WriteLine(Math.E);    // 2.71828182845905

// Вычисляем длину окружности:
double radius = 5.0;
double circumference = 2 * Math.PI * radius;
Console.WriteLine($"Длина окружности: {circumference:F2}");  // 31.42
🎲

Случайные числа: класс Random

Случайные числа нужны в играх, тестировании, симуляциях и даже в криптографии. В C# за генерацию случайных чисел отвечает класс Random. Он «псевдослучайный» — числа выглядят случайными, но вычисляются по формуле.

Основные методы класса Random:

Метод Описание Пример
Next()Любое целое от 0 до 2 147 483 646rnd.Next()
Next(max)От 0 до max-1rnd.Next(10) → 0..9
Next(min, max)От min до max-1rnd.Next(1, 7) → 1..6 (как кубик)
NextDouble()Дробное от 0.0 до 1.0 (не включая 1.0)rnd.NextDouble()
C# · Random — генерация случайных чисел
// Создаём генератор случайных чисел
Random rnd = new Random();

// Целое число от 0 до 99
int randomInt = rnd.Next(100);
Console.WriteLine($"Случайное 0..99: {randomInt}");

// Кубик D6 (1 до 6)
int dice = rnd.Next(1, 7);
Console.WriteLine($"Кубик: {dice}");

// Дробное число 0.0..1.0
double randomDouble = rnd.NextDouble();
Console.WriteLine($"Дробное: {randomDouble:F4}");

// Случайное число от 5.0 до 10.0
double customDouble = 5.0 + rnd.NextDouble() * 5.0;
Console.WriteLine($"Дробное 5..10: {customDouble:F2}");

⚠️ Типичные ошибки с Random:

  • Создавать новый Random в цикле. Если создать два Random подряд в быстрой последовательности — оба выдадут одинаковые числа, потому что инициализируются одинаковым значением системного таймера.
  • Забыть про диапазон. rnd.Next(1, 7) даёт числа от 1 до 6 (7 не входит). Запомни: первый включён, второй исключён.
  • Путать NextDouble и Next. NextDouble не принимает аргументов и даёт 0.0..1.0. Next с одним аргументом даёт 0..max-1.
💡 В реальных проектах используй один экземпляр Random на всю программу (можно сделать его статическим). Если нужна криптостойкая случайность — используй System.Security.Cryptography.RandomNumberGenerator, но это тема продвинутого уровня.
🧪

Мини-тест: проверь себя

Попробуй ответить на вопросы. Это поможет закрепить материал.

Вопрос 1: Чему равно 17 % 5?

A) 3 B) 2 C) 3.4 D) 1

✅ Ответ: B (17 = 5*3 + 2, остаток 2)

Вопрос 2: Что выведет этот код?

int a = 7;
int b = 2;
double result = a / b;
Console.WriteLine(result);
A) 3.5 B) 3 C) 3.0 D) ошибка

✅ Ответ: C (7 / 2 = 3 — целочисленное деление, потом 3 → 3.0)

Вопрос 3: Что вернёт Math.Ceiling(-2.3)?

A) -3 B) -2 C) 2 D) 3

✅ Ответ: B (-2. Ceiling — это «потолок», т.е. наименьшее целое, которое >= -2.3. Это -2.)

Вопрос 4: Как получить случайное число от 50 до 100 (включительно)?

A) rnd.Next(50, 101) B) rnd.Next(50, 100) C) rnd.Next(50, 99) D) rnd.Next(50) + 50

✅ Ответ: A (100 включительно, поэтому max = 101, т.к. второй аргумент не включается.)

Вопрос 5: Что выведет этот код?

int x = 5;
int y = ++x * 2;
Console.WriteLine($"{x} {y}");
A) 5 10 B) 6 10 C) 6 12 D) 5 12

✅ Ответ: C (++x сначала увеличивает x до 6, потом 6 * 2 = 12.)

📝

Практическое задание

Напиши программу «Калькулятор здоровья». Она должна:

  • Запросить у пользователя имя, возраст, рост (в см) и вес (в кг)
  • Вычислить ИМТ (индекс массы тела): вес / (рост в метрах)2
  • Округлить ИМТ до одного знака после запятой
  • Вычислить, сколько лет осталось до 100 лет
  • Определить, чётный или нечётный возраст пользователя
  • Сгенерировать случайное «число здоровья» от 1 до 100 и вывести его
  • Использовать класс Math (хотя бы Sqrt, Pow, Round)
👀 Показать решение
using System;

class Program
{
    static void Main()
    {
        Console.Write("Введите имя: ");
        string name = Console.ReadLine();

        Console.Write("Введите возраст: ");
        int age = int.Parse(Console.ReadLine());

        Console.Write("Введите рост (см): ");
        double heightCm = double.Parse(Console.ReadLine());

        Console.Write("Введите вес (кг): ");
        double weight = double.Parse(Console.ReadLine());

        // Расчёт ИМТ
        double heightM = heightCm / 100.0;
        double bmi = weight / Math.Pow(heightM, 2);
        bmi = Math.Round(bmi, 1);

        // До 100 лет
        int yearsTo100 = 100 - age;

        // Чётность
        bool isEven = age % 2 == 0;

        // Случайное число здоровья
        Random rnd = new Random();
        int healthScore = rnd.Next(1, 101);

        // Вывод
        Console.WriteLine($"{"=".PadRight(45, '=')}");
        Console.WriteLine($"  Здоровье: {name}");
        Console.WriteLine($"{"=".PadRight(45, '=')}");
        Console.WriteLine($"  Возраст:         {age} лет {(isEven ? "(чётный)" : "(нечётный)")}");
        Console.WriteLine($"  До 100 лет:      {yearsTo100} лет");
        Console.WriteLine($"  ИМТ:             {bmi}");
        Console.WriteLine($"  Здоровье:        {healthScore}/100");
        Console.WriteLine($"{"=".PadRight(45, '=')}");
    }
}
🧩

Всё вместе — симулятор битвы

Напишем программу, которая симулирует один раунд битвы между героем и монстром. Используем все изученные концепции: арифметику, Math, Random, инкременты, составные операторы.

C# · Симулятор боя
using System;

class Program
{
    static void Main()
    {
        Random rnd = new Random();
        
        // Характеристики героя
        string heroName = "Артур";
        int heroHp = 100;
        int heroAttack = 20;
        int heroDefense = 10;

        // Характеристики монстра
        string monsterName = "Гоблин";
        int monsterHp = 80;
        int monsterAttack = 15;
        int monsterDefense = 5;

        Console.WriteLine($"⚔️ {heroName} против {monsterName}!");
        Console.WriteLine($"{"=".PadRight(40, '=')}");

        // Раунд атаки героя
        int heroRoll = rnd.Next(1, 21);                   // бросок удачи 1..20
        int heroDamage = heroAttack + heroRoll;           // урон = атака + бросок
        int actualDamage = Math.Max(1, heroDamage - monsterDefense); // с учётом защиты
        monsterHp -= actualDamage;                         // составной оператор
        monsterHp = Math.Max(0, monsterHp);               // не ниже 0

        Console.WriteLine($"{heroName} наносит {actualDamage} урона!");
        Console.WriteLine($"У {monsterName} осталось {monsterHp} HP");

        // Раунд атаки монстра (если жив)
        if (monsterHp > 0)
        {
            int monsterRoll = rnd.Next(1, 21);
            int monsterDamage = monsterAttack + monsterRoll;
            int actualMonsterDmg = Math.Max(1, monsterDamage - heroDefense);
            heroHp -= actualMonsterDmg;
            heroHp = Math.Max(0, heroHp);

            Console.WriteLine($"{monsterName} наносит {actualMonsterDmg} урона!");
            Console.WriteLine($"У {heroName} осталось {heroHp} HP");
        }

        // Итог
        Console.WriteLine($"{"=".PadRight(40, '=')}");
        if (monsterHp <= 0)
            Console.WriteLine($"🏆 {heroName} победил!");
        else if (heroHp <= 0)
            Console.WriteLine($"💀 {monsterName} победил...");
        else
            Console.WriteLine($"🤝 Оба живы. Следующий раунд!");

        // Статистика
        Console.WriteLine($"{"=".PadRight(40, '=')}");
        Console.WriteLine($"Итоговое HP: {heroName}={heroHp}, {monsterName}={monsterHp}");

        // Процент оставшегося здоровья
        double heroPercent = Math.Round((double)heroHp / 100 * 100, 1);
        double monsterPercent = Math.Round((double)monsterHp / 80 * 100, 1);
        Console.WriteLine($"Здоровье: {heroPercent}% vs {monsterPercent}%");
    }
}
💡 Обрати внимание: Math.Max(0, monsterHp) гарантирует, что HP не уйдёт в минус. Math.Max(1, heroDamage - monsterDefense) гарантирует минимум 1 урона. Это идиоматические приёмы в C#.
📌

Что важно запомнить

1️⃣
int / int = int (дробная часть отбрасывается). Чтобы получить дробь, хотя бы один операнд должен быть double.
2️⃣
% — остаток от деления. Незаменим для проверки чётности, циклических счётчиков и группировки.
3️⃣
++ и -- увеличивают/уменьшают на 1. Префикс (++x) возвращает новое значение, постфикс (x++) — старое.
4️⃣
Составные операторы (+=, -=, *=, /=, %=) сокращают x = x + y → x += y.
5️⃣
Класс Math: Abs (модуль), Max/Min (макс/мин), Pow (степень), Sqrt (корень), Round/Floor/Ceiling (округление).
6️⃣
Random: rnd.Next(1, 7) — целое от 1 до 6. Создавай один Random на всю программу.
7️⃣
Math.PI и Math.E — константы для числа π и экспоненты.

Тест: 2.4: Работа с числами

7 вопросов

Арифметика

Premium