Числа и арифметика

Разбираемся, как Kotlin хранит числа и считает.

Числовые типы в Kotlin различаются разрядностью и тем, целое это число или дробное: Int, Long, Double и другие.

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

Основные числовые типы

ТипНазначение
Intцелые числа (32 бита)
Longбольшие целые (64 бита)
Doubleдробные числа двойной точности
Floatдробные одинарной точности

По умолчанию целое число — это Int, а дробное — Double. Чтобы задать Long, добавляют суффикс L: 10L.

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

Если оба операнда целые, деление тоже целое — дробная часть отбрасывается.

fun main() {
    println(7 / 2)        // целочисленное деление
    println(7 % 2)        // остаток
    println(7.0 / 2)      // дробное деление
}

Вывод:

3
1
3.5

Преобразования типов

Kotlin не делает неявных преобразований между числовыми типами — это защищает от случайной потери данных. Преобразовывать нужно явно методами toInt(), toDouble(), toLong() и так далее.

fun main() {
    val a = 5
    val b = 2.0
    val result = a.toDouble() / b
    println(result)
    val rounded = 3.9.toInt()   // отбрасывает дробь
    println(rounded)
}

Вывод:

2.5
3

Как работает под капотом

Тип Int занимает 32 бита и вмещает числа примерно до 2,1 миллиарда. Если выйти за этот предел, происходит переполнение: значение «заворачивается» в отрицательную область, без ошибки. Для заведомо больших чисел берите Long. А toInt() у дробного просто отбрасывает дробную часть, а не округляет — для округления есть отдельные функции Math.round или метод roundToInt().

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

  • Ждать дробь от целочисленного деления. 7 / 2 равно 3, а не 3.5 — хотя бы один операнд должен быть Double.
  • Рассчитывать на автоприведение типов. Kotlin требует явного toDouble() / toInt().
  • Игнорировать переполнение Int. Для очень больших значений используйте Long.

Итог

  • Целое по умолчанию — Int, дробное — Double.
  • Деление двух целых даёт целое; для дроби сделайте операнд Double.
  • Преобразования типов — только явные: toInt(), toDouble() и т. п.
  • toInt() отбрасывает дробную часть, а не округляет.
Проверьте себя
1. Чему равно выражение 7 / 2 в Kotlin?
A3.5
B3
C4
DОшибка компиляции
2. Что вернёт 3.9.toInt()?
A4 (округление к ближайшему)
B3 (отбрасывание дробной части)
C3.9
DОшибку
3. Почему Kotlin требует явного a.toDouble() при делении Int на Double?
AЧтобы код был длиннее
BЧтобы избежать случайной потери данных из-за неявных преобразований
CЭто ошибка дизайна языка
DDouble не умеет делиться на Int