Skip to content

Основы работы с числами

Основы работы с числами в языке Dart

В Dart есть встроенные типы данных — built-in types.

Сегодня мы рассмотрим работу с числами и основные операции над ними.

В Dart есть два типа чисел: целые (int) и дробные (double). На виртуальной машине Dart (Dart VM) int может хранить целые числа до 64 бит. теоретически это от -9223372036854775808 до 9223372036854775807. Не забывайте, что один бит уходит на знак числа, поэтому максимальное значение int равно 2^63 - 1. Ещё мы теряем на запятой в double, поэтому максимальное значение double равно скорее 1.7976931348623157E+308 или (2^52). И если мы компилируем для веб-приложения в javascript, то должны знать, что int там теряет точность после 2^53.

Рассмотрим их на практике.

Приложение на Dart начинается с метода main, который является основным методом приложения.

Метод main — это точка входа в приложение. Здесь начинается выполнение программы.

void main() {
}

В этом методе мы можем сразу написать небольшой код либо вызвать другие методы или функции. В Dart методы — это функции, которые определяются внутри классов, но в контексте базового примера различие не столь важно. Мы подробнее рассмотрим это различие при углубленном изучении ООП.

Итак, в методе main мы можем объявить пару переменных и, например, сложить их.

void main() {
  int a = 5;
  int b = 3;
  int sum = a + b;
  print(sum);
}

Если вывод результата сложения двух чисел занимает 4 строчки, то небольшое приложение, например, погоды, может занять и несколько тысяч строк.

Чтобы разобраться с этими строчками, мы работаем с различными функциями, которые логически отделены друг от друга.

void main() {
  addInts();
  addDoubles();
}
 
void addInts() {
  int a = 5;
  int b = 3;
  int sum = a + b;
  print("Результат сложения целых чисел: $sum");
}
 
void addDoubles() {
  double a = 5.5;
  double b = 3.4;
  double sum = a + b;
  print("Результат сложения десятичных чисел: $sum");
}

В методе main вызываются две функции: addInts и addDoubles. Одна складывает заранее определенные целые числа, другая — десятичные.

Многие считают, что в человеческом понимании числа не делятся на числа с запятой и без. На самом деле это не совсем так. Для компьютера важно различать целые и дробные числа, поскольку операции с ними могут отличаться.

Теперь давайте реализуем простой калькулятор, чтобы понять, как работает ввод данных от пользователя.

import 'dart:io'; // Эта строчка очень важна, без неё не будет работать ввод данных
 
// Функция сложения двух чисел
double addNumbers(double a, double b) {
  return a + b;
}
 
double userInput(order) {
  stdout.write("Введите $order число: ");
  double num = double.parse(stdin.readLineSync()!);
  return num;
}
 
void main() {
  // Запрос первого числа
  double num1 = userInput("первое");
  // Запрос второго числа
  double num2 = userInput("второе");
 
  // Вычисление и вывод результата
  double result = addNumbers(num1, num2);
  print("Сумма: $result");
}

После запуска в терминале у нас будет примерно такой вывод:

Введите число: 12
Введите число: 13
Сумма: 25.0
  1. Мы создали функцию addNumbers, которая принимает два числа и возвращает их сумму.
  2. Функция userInput запрашивает у пользователя ввод числа и возвращает его.
  3. В методе main мы вызываем функцию userInput дважды, чтобы получить два числа, складываем их и выводим результат.
  • Мы переиспользовали одну функцию дважды.
  • Мы сделали функции универсальными: каждый раз пользователь может вводить разные числа.
  • Работа с вводом пользователя тоже достаточно универсальна.
  • И мы сделали свой первый калькулятор. Абсолютно простейший, как амёба, но он работает, и это наш калькулятор.

Давайте посмотрим, что ещё мы можем делать с числами.

void main() {
  int a = 123456789;
  print(a);
  double b = 0.123456;
  print(b);
}

Нижние подчеркивания игнорируются компьютером, они просто для удобного чтения. Большие числа, как 4611686018427387904, тяжело прочитать. Нижние подчеркивания не так давно вошли практически во все языки программирования. В Java они более 10 лет, в JS — около 5 лет.

У нас есть возможность простой работы с шестнадцатеричной системой счисления.

Работа с различными системами счисления

Используется префикс 0x:

void main() {
  int hexValue = 0x100; // 256 в десятичной системе
  print(hexValue); // Выведет: 256
}

Для работы в других системах надо использовать метод toRadixString:

// Binary (base 2).
print(12.toRadixString(2)); // 1100
print(31.toRadixString(2)); // 11111
print(2021.toRadixString(2)); // 11111100101
print((-12).toRadixString(2)); // -1100
// Octal (base 8).
print(12.toRadixString(8)); // 14
print(31.toRadixString(8)); // 37
print(2021.toRadixString(8)); // 3745
// Hexadecimal (base 16).
print(12.toRadixString(16)); // c
print(31.toRadixString(16)); // 1f
print(2021.toRadixString(16)); // 7e5
// Base 36.
print((35 * 36 + 1).toRadixString(36)); // z1

Простые вычисления с помощью Dart

Но давайте вернёмся к обычной десятичной системе счисления. И посмотрим как мы можем решать к примеру обычные школьные задачи с помощью Dart.

Сложение и вычитание чисел

void main() {
  int a = 5;
  int b = 3;
  int sum = a + b;
  int diff = a - b;
  print("Сумма: $sum");
  print("Разность: $diff");
}

Умножение и деление чисел

void main() {
  int a = 5;
  int b = 3;
  int product = a * b;
  int quotient = a / b;
  print("Произведение: $product");
  print("Частное: $quotient");
}

Умножение и деление дробных чисел

void main() {
  double a = 5.5;
  double b = 3.4;
  double product = a * b;
  double quotient = a / b;
  print("Произведение: $product");
  print("Частное: $quotient");
}

Давайте посмотрим, что ещё мы можем делать с числами.

void main() {
  const double pi = 3.14;
  // const - используется для объявления константы,
  // теперь переменной PI нельзя присвоить другое значение.
  double r = 5.5;
  double k = r * r;
  double s = pi * r * r;
  print(k);
  // промежуточный результат
  print(s);
}

Обратите внимание на то, что const не даёт изменить значение переменной. Для тех, кто имеет опыт программирования, обратите внимание, что pi пишется в нижнем регистре, а не с заглавной буквы. "Constants in Dart are written in lowercase_with_underscores."

Экспоненциальная запись

Экспоненциальная запись — представление действительных чисел в виде мантиссы (дробной части логарифма числа) и порядка. Удобна при представлении очень больших и очень малых чисел, а также для унификации их написания.

void main() {
    double a = 1.42e5; // 142000.0
    double b = 1.42e-5; // 0.0000142
    print(a);
    print(b);
 }

Достаточно интересный вопрос: что происходит при пересечении разных типов данных? Что произойдёт, если тип переменных int встретится с double? Программа ниже демонстрирует, как ведут себя разные типы данных в Dart.

import 'dart:math';
 
void main() {
  int var1 = 5;
  int var2 = 2;
  int var3 = var1 ~/ var2;
 
  double var4 = 5.0;
  double var5 = 2.0;
  double var6 = var4 / var5;
  double var7 = var1 / var2;
 
  double var25 = var1 / var4;
 
  print("Int Var3 = \$var3");
  print("double Var6 = \$var6");
  print("double Var7 = \$var7");
  print("double Var25 = \$var25");
 
  int var11 = (var1 / var4).toInt();
 
  print("Int Var11 = \$var11");
  print("Магия = \${5.0 / var2}");
}
  • Оператор деления "/" возвращает всегда double.
  • Оператор "~/" возвращает целую часть от деления и позволяет работать с int.

Результаты работы программы показывают, что переменная типа int может автоматически использоваться в расчётах с double. Однако обратное преобразование требует явного преобразования (casting), как в случае с var11.

Теперь рассмотрим генерацию случайного числа от 30 до 100 в Dart. В отличие от Java, где используется Math.random(), в Dart есть класс Random():

void main() {
  int min = 30;
  int max = 100;
  int result = Random().nextInt(15);
  double doubleValue = Random().nextDouble();
  int secureResult = Random().secure().nextInt(10);
  print("Случайное число: \$result");
}

Метод secure() использует такие источники, что делает число, сгенерированное этим методом, трудно предсказуемым даже если злоумышленник имеет доступ к предыдущим числам.

Округление чисел

В Dart существует несколько способов округления чисел, в том числе с помощью методов ceil(), floor(), round(), и truncate(). Давайте рассмотрим каждый из них на практике.

Метод ceil()

Метод ceil() округляет число в большую сторону до ближайшего целого числа.

Пример:

void main() {
  double num = 3.2;
  print(num.ceil()); // 4
}

Метод floor()

Метод floor() округляет число в меньшую сторону до ближайшего целого числа.

Пример:

void main() {
  double num = 3.8;
  print(num.floor()); // 3
}

Метод round()

Метод round() округляет число до ближайшего целого. Если дробная часть числа меньше 0.5, число округляется в меньшую сторону, если больше или равно 0.5 — в большую сторону.

Пример:

void main() {
  double num1 = 3.4;
  double num2 = 3.5;
  print(num1.round()); // 3
  print(num2.round()); // 4
}

Метод truncate()

Метод truncate() удаляет дробную часть числа и оставляет только целую. Это можно рассматривать как округление к нулю.

Пример:

void main() {
  double num = 3.8;
  print(num.truncate()); // 3
}

Возведение в степень

В Dart возведение в степень выполняется с помощью оператора pow из библиотеки dart:math. Он позволяет возвести число в степень и возвращает результат в виде числа типа double. Рассмотрим пример использования.

Пример возведения в степень:

import 'dart:math';
 
void main() {
  double base = 2;
  int exponent = 3;
  double result = pow(base, exponent);
 
  print("Результат: $result"); // 8.0
}

Здесь мы используем функцию pow(base, exponent), где base — основание, а exponent — показатель степени. В данном примере основание 2 возводится в степень 3, и результат равен 8.0.

Использование с целыми числами

Важно отметить, что результат всегда будет типа double, даже если основание и показатель степени — целые числа. Например:

import 'dart:math';
 
void main() {
  int base = 2;
  int exponent = 3;
  double result = pow(base, exponent);
 
  print("Результат: $result"); // 8.0
}

Если вам нужно получить целое число, можно использовать приведение типов:

import 'dart:math';
 
void main() {
  int base = 2;
  int exponent = 3;
  int result = pow(base, exponent).toInt();
 
  print("Результат: $result"); // 8
}

Домашнее задание

Может показаться, что слишком много математики, да и домашнего задания, но нам за один урок надо повторить математику средней школы. И скорее всего, этого даже недостаточно для среднего новичка. Поэтому решайте сами, как глубоко вам нужно. Я рекомендую выделить пару часов и прорешать все или хотя бы большинство задач.

1. Найти площадь и периметр квадрата. Задайте сторону квадрата и вычислите его площадь и периметр, используя тип данных int или double.

calculateSquare(4) → "Площадь: 16, Периметр: 16"
calculateSquare(5.5) → "Площадь: 30.25, Периметр: 22.0"

2. Найти площадь и периметр прямоугольника. Для прямоугольника задайте длину и ширину, и вычислите его площадь и периметр.

calculateRectangle(4, 5) → "Площадь: 20, Периметр: 18"
calculateRectangle(3.5, 6) → "Площадь: 21.0, Периметр: 19.0"

3. Найти площадь и периметр куба. Задайте длину ребра куба и вычислите площадь его поверхности и объём, используя тип данных double или int.

calculateCube(3) → "Площадь: 54, Объем: 27"
calculateCube(2.5) → "Площадь: 37.5, Объем: 15.625"

4. Вычисление площади круга Напишите программу, которая принимает радиус круга и выводит его площадь. Для вычисления используйте формулу S = π * r^2, где r радиус круга, а pi — математическая константа.

calculateCircleArea(5) → "Площадь: 78.53981633974483"
calculateCircleArea(2.5) → "Площадь: 19.634954084936208"

5. Длина окружности Напишите программу, которая по заданному радиусу круга находит его длину. Для вычисления используйте формулу L = 2 * pi * r, где r — радиус круга.

calculateCircleLength(5) → "Длина: 31.41592653589793"
calculateCircleLength(2.5) → "Длина: 15.707963267948966"

6. Конвертер валют Напишите программу, которая переводит сумму в долларах в евро. Известно, что курс евро к доллару составляет 1.1. Программа должна принимать на вход сумму в долларах и выводить эквивалент в евро.

convertDollarsToEuros(100) → "Евро: 110.0"
convertDollarsToEuros(50.5) → "Евро: 55.55"

7. Сравнение площадей стран Рассчитайте, во сколько раз площадь Беларуси больше площади Украины. Площадь Беларуси — 207,600 км², площадь Украины — 603,500 км². Программа должна вывести результат в виде целого числа.

8. Площадь городов Сравните площадь Москвы и Берлина. Площадь Москвы — 2,511 км², площадь Берлина — 891.8 км². Найдите, во сколько раз площадь Москвы больше площади Берлина.

9. Проценты в банке (Простой процент) Если клиент положил в банк 1000 евро под 3.5% годовых, рассчитайте, сколько денег будет на его счету через 7 лет. Для этого используйте формулу простого процента: S = P * (1 + (r-t)/100), где P — начальная сумма, r — процентная ставка, t — количество лет.


Задачи повышенной сложности

10. Проценты в банке (Сложный процент) Если клиент положил 1000 евро под 3.5% годовых с капитализацией раз в год, рассчитайте, сколько денег будет на его счету через 7 лет. Для этого используйте формулу сложного процента: S = P * (1 + r)/100)^t, где P — начальная сумма, r — процентная ставка, t — количество лет.

11. Найти количество зёрен на шахматной доске. Шахматная доска состоит из 64 клеток. На первую клетку положите одно зёрнышко, на вторую — два, на третью — четыре и так далее, удваивая количество зёрен на каждой следующей клетке. Напишите программу, которая вычислит общее количество зёрен на всей доске.

Дополнительные ссылки

  1. num class
  2. Match class
  3. Effective Dart: Style
  4. Dart:math library
  5. Bit - https://en.wikipedia.org/wiki/Bit
  6. Системы счисления
  7. Легенда о шахматах и зёрнах
14 февр. 2025 г.
andron13