$ sudo teach IT

Зачем компьютеру разные типы чисел

В жизни вы не путаете «штуки» и «граммы»: пять яблок и пять с половиной килограммов — это разные по природе величины, и складывать их напрямую бессмысленно. У чисел в программе то же самое: одни числа всегда целые (количество товаров, номер строки, счётчик), а другие — дробные (цена, вес, средний балл). Swift различает эти два случая на уровне типов и не даёт случайно перепутать одно с другим — это и убережёт вас от ошибок, и первое время будет немного раздражать. В этом уроке разберём основные числовые типы, логический тип Bool и то, как вставлять значения переменных прямо в текст.

Int, Double и их младшие родственники

Целые числа в Swift имеют тип Int: это количество, номер, индекс — всё, что не бывает дробным по смыслу.

let studentsCount: Int = 24
let floor = -2
print(studentsCount)

Дробные числа получают тип Double — это основной тип для чисел с плавающей точкой в Swift, используется почти везде: цены, координаты, проценты.

let price: Double = 149.90
let average = 4.75
print(price)

Есть ещё Float — тоже дробное число, но с меньшей точностью и меньшим расходом памяти. На практике в обычных приложениях почти всегда берут Double, а Float нужен редко, например для очень больших объёмов данных или там, где важна каждая единица памяти. Пока достаточно знать, что такой тип существует и выглядит он так же, как Double, только с явной аннотацией: let rating: Float = 4.5.

Символ — это тип Character, он хранит ровно один печатаемый символ (букву, цифру, знак) и записывается через двойные кавычки, как строка:

let grade: Character = "A"
let sign: Character = "!"

Если попробовать положить в Character несколько символов, компилятор откажется: этот тип рассчитан строго на один символ. Про строки и работу с текстом целиком поговорим отдельно, здесь Character упомянут только как сосед по семье базовых типов.

Читаемые литералы

Когда число длинное, его тяжело читать глазами: 1000000 — это миллион или сто тысяч, сходу не разберёшь. Swift разрешает вставлять подчёркивания между цифрами — компилятор их просто игнорирует, а человеку читать становится сильно проще:

let population: Int = 1_000_000
let price: Double = 12_345.67

Кроме привычной десятичной записи, целые числа можно писать в двоичной форме с префиксом 0b и в шестнадцатеричной с префиксом 0x. Это пригодится в будущем при работе с байтами и цветами, а пока достаточно узнавать такую запись:

let binaryFlags = 0b1010 // это число 10
let colorCode = 0xFF // это число 255

Swift не смешивает типы молча

Вот здесь начинается самое важное. В некоторых языках можно сложить целое число и дробное, и язык сам «дотянет» одно до другого. Swift так не делает — и не потому, что придирается, а потому что при таком автоматическом преобразовании легко незаметно потерять точность или получить не то поведение, которого вы ждали. Поэтому компилятор требует явного преобразования типов.

let itemsCount: Int = 3
let pricePerItem: Double = 2.5

// let total = itemsCount + pricePerItem // ошибка компиляции
let total = Double(itemsCount) * pricePerItem
print(total)

Строка Double(itemsCount) — это не «превращение» переменной, а создание нового значения типа Double из значения itemsCount. Сама переменная itemsCount так и остаётся Int. Именно такое явное преобразование Swift требует каждый раз, когда вы смешиваете Int и Double в одном выражении — сложении, вычитании, умножении или делении.

Целочисленное деление — главная ловушка новичка

Деление — тот случай, где типы решают всё. Если оба числа — Int, результат деления тоже будет Int, и дробная часть просто отбрасывается, без округления:

let a = 7
let b = 2
print(a / b)

Здесь напечатается 3, а не 3.5: деление целых чисел в Swift называется целочисленным, и половина между 3 и 4 никуда не округляется — она просто теряется. Если хотя бы одно из чисел — Double, результат тоже становится Double, и дробная часть сохраняется:

print(7.0 / 2)

Тут результат — 3.5. Обратите внимание: 2 здесь без точки, но так как второй операнд 7.0 уже Double, Swift сам подбирает подходящий тип для литерала 2 — конкретно для числовых литералов без переменной это разрешено, в отличие от сложения двух уже существующих переменных разных типов. Если же оба значения хранятся в переменных типа Int, а вам нужен точный дробный результат, переводите в Double явно, как в примере с преобразованием выше.

Остаток от деления

Оператор % возвращает остаток от целочисленного деления — пригождается, чтобы проверить чётность числа, разложить секунды на минуты или распределить элементы по группам:

let seconds = 130
let minutes = seconds / 60
let leftoverSeconds = seconds % 60
print(minutes)
print(leftoverSeconds)

130 секунд — это 2 полных минуты (130 / 60) и ещё 10 секунд остатка (130 % 60). Деление и остаток почти всегда идут в паре именно так.

Строковая интерполяция

Чтобы вставить значение переменной прямо в текст, не нужно склеивать строки — используется интерполяция: внутри строки пишете обратный слэш, круглые скобки, а в них — что угодно, что можно вычислить.

let name = "Аня"
let age = 21
print("Привет, \(name)! Тебе \(age) лет.")

Важно, что внутри скобок необязательно должна быть просто переменная — это может быть целое выражение, которое Swift вычислит на месте перед тем, как собрать строку:

let width = 5
let height = 3
print("Площадь: \(width * height)")

Здесь Swift сначала посчитает width * height, получит 15, а затем подставит результат в строку — переменная с промежуточным значением для этого не нужна.

Bool и условия

Bool — логический тип с ровно двумя значениями: true и false. Он отвечает на вопросы вида «да или нет», «выполнено условие или нет»:

let isRegistered: Bool = true
let hasDiscount = false
print(isRegistered)

Ключевая особенность Swift: условие в конструкции if обязано быть именно значением типа Bool, и никак иначе. В некоторых языках вместо условия можно подставить число, и ноль считается «ложью», а всё остальное — «правдой». В Swift так не работает:

let attemptsLeft = 1

// if attemptsLeft { ... } // ошибка компиляции: Int — не Bool
if attemptsLeft > 0 {
    print("Можно ещё попробовать")
}

Оператор > из последнего примера и другие способы получить Bool из сравнения разберём отдельно и подробно — здесь достаточно запомнить само правило: условие — это всегда Bool, число вместо него компилятор не примет ни при каких обстоятельствах.

Частые ошибки

  • Попытка сложить Int и Double напрямую — компилятор потребует обернуть целое число в Double(...).
  • Ожидание, что 7 / 2 даст 3.5 — если оба числа Int, дробная часть отбрасывается, получится 3.
  • Забыть, что для дробного результата достаточно, чтобы Double-типом был хотя бы один из операндов, но если оба операнда — переменные типа Int, преобразовывать нужно явно.
  • Пытаться использовать число как условие в if — в Swift сработает только настоящий Bool.
  • Забыть закрывающую скобку в интерполяции \(...) — компилятор укажет на ошибку синтаксиса строки.

Резюме

  • Int — целые числа, Double — дробные, Bool — логическое да/нет, Character — один символ, Float — редко используемая дробная альтернатива с меньшей точностью.
  • В больших числах можно ставить подчёркивания для читаемости: 1_000_000; есть также двоичная (0b) и шестнадцатеричная (0x) запись.
  • Swift не складывает Int и Double молча — нужен явный Double(x), это защита от потери точности, а не прихоть языка.
  • Деление двух Int целочисленное: 7 / 2 даёт 3; чтобы получить 3.5, хотя бы одно значение должно быть Double.
  • Оператор % возвращает остаток от деления.
  • Интерполяция \(...) вставляет в строку значение и может содержать целое выражение, а не только имя переменной.
  • Условие в if обязано быть типом Bool — число вместо него не подходит.

Проверьте себя

4 вопроса

Стоимость подписки

Напишите программу, которая объявляет количество месяцев подписки как значение типа Int, равное 6, и цену одного месяца как значение типа Double, равное 4.5. Посчитайте полную стоимость подписки, явно преобразовав количество месяцев в Double перед умножением, и напечатайте результат одной командой print.

Программа должна вывести ровно: 27.0

Яблоки по коробкам

Есть 17 яблок, а в одну коробку помещается 5 яблок. Объявите обе величины как Int, а затем одной строкой print с интерполяцией выведите, сколько получится полных коробок и сколько яблок останется. Деление и остаток нужно посчитать прямо внутри интерполяции — без промежуточных переменных для результата деления и остатка.

Программа должна вывести ровно: Целых коробок: 3, свободных яблок: 2