Числа
Целочисленные типы
Kotlin предоставляет набор встроенных типов, представляющих числа.
Для целых чисел существуют четыре типа с различными размерами и, следовательно, диапазонами значений:
Тип |
Размер (биты) |
Минимальное значение |
Максимальное значение |
|---|---|---|---|
|
8 |
-128 |
127 |
|
16 |
-32768 |
32767 |
|
32 |
-2 147 483 648 (-231) |
2 147 483 647 (231 - 1) |
|
64 |
-9 223 372 036 854 775 808 (-263) |
9 223 372 036 854 775 807 (263 - 1) |
При инициализации переменной без явного указания типа компилятор автоматически определяет тип с наименьшим диапазоном, достаточным для представления значения. Если оно не превышает диапазон Int, тип будет Int. Если превышает, тип будет Long. Чтобы явно указать тип Long значения, добавьте суффикс L к значению. Явное указание типа заставляет компилятор проверить, не превышает ли значение диапазон указанного типа.
val one = 1 // Int val threeBillion = 3000000000 // Long val oneLong = 1L // Long val oneByte: Byte = 1
Типы с плавающей точкой
Для вещественных чисел Kotlin предоставляет типы с плавающей точкой Float и Double, которые соответствуют стандарту IEEE 754. Float отражает стандарт IEEE 754 single precision, в то время как Double отражает double precision.
Эти типы отличаются по размеру и обеспечивают хранение чисел с плавающей точкой с различной точностью:
Тип |
Размер (биты) |
Значимые биты |
Биты экспоненты |
Десятичные цифры |
|---|---|---|---|---|
|
32 |
24 |
8 |
6-7 |
|
64 |
53 |
11 |
15-16 |
Вы можете инициализировать переменные Double и Float числами с дробной частью. Она отделена от целой части точкой (.) Для переменных, инициализированных дробными числами, компилятор определяет тип Double:
val pi = 3.14 // Double // val one: Double = 1 // Error: type mismatch val oneDouble = 1.0 // Double
Чтобы явно указать тип Float значения, добавьте суффикс f или F. Если такое значение содержит более 6-7 десятичных знаков, оно будет округлено:
val e = 2.7182818284 // Double val eFloat = 2.7182818284f // Float, actual value is 2.7182817
В отличие от некоторых других языков, в Kotlin нет неявных расширений преобразований для чисел. Например, функцию с параметром Double можно вызвать только с значениями Double, но не с Float, Int, или другими числовыми значениями:
fun main() {
fun printDouble(d: Double) { print(d) }
val i = 1
val d = 1.0
val f = 1.0f
printDouble(d)
// printDouble(i) // Error: Type mismatch
// printDouble(f) // Error: Type mismatch
}
Для преобразования числовых значений в другие типы используйте явные преобразования.
Литералы чисел
Существуют следующие виды литералов целых значений:
Десятичные:
123Longs помечаются заглавной буквой
L:123LШестнадцатеричные:
0x0FДвоичные:
0b00001011
Kotlin также поддерживает обычную запись для чисел с плавающей точкой:
Doubles по умолчанию:
123.5,123.5e10Floats помечаются
fилиF:123.5f
Можно использовать нижние подчёркивания, чтобы сделать константы чисел более читабельными:
val oneMillion = 1_000_000 val creditCardNumber = 1234_5678_9012_3456L val socialSecurityNumber = 999_99_9999L val hexBytes = 0xFF_EC_DE_5E val bytes = 0b11010010_01101001_10010100_10010010
Представление чисел на JVM
На платформе JVM числа хранятся как примитивные типы: int, double, и так далее. Исключение составляют случаи, когда вы создаёте ссылку на число с возможностью null, например, Int?, или используете дженерики. В этих случаях числа упаковываются в классы Java Integer, Double, и так далее.
Ссылки с возможностью null на одно и то же число могут ссылаться на разные объекты:
fun main() {
//sampleStart
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false
//sampleEnd
}
Все ссылки с возможностью null на a фактически являются одним и тем же объектом из-за оптимизации памяти, которую JVM применяет к Integer между -128 и 127. Она не применяется к ссылкам b, поэтому они являются разными объектами.
Однако они по-прежнему равны:
fun main() {
//sampleStart
val b: Int = 10000
println(b == b) // Prints 'true'
val boxedB: Int? = b
val anotherBoxedB: Int? = b
println(boxedB == anotherBoxedB) // Prints 'true'
//sampleEnd
}
Явные преобразования чисел
Из-за разных представлений, типы меньшего размера не являются подтипами больших. Если бы они были, у нас возникли бы трудности следующего рода:
// Hypothetical code, does not actually compile: val a: Int? = 1 // A boxed Int (java.lang.Integer) val b: Long? = a // Implicit conversion yields a boxed Long (java.lang.Long) print(b == a) // Surprise! This prints "false" as Long's equals() checks whether the other is Long as well
Поэтому равенство было бы потеряно незаметно, не говоря уже об идентичности.
Вследствие этого, типы меньшего размера не преобразуются неявно в типы большего размера. Это означает, что присваивание значения типа Byte переменной типа Int требует явного преобразования:
fun main() {
//sampleStart
val b: Byte = 1 // OK, literals are checked statically
// val i: Int = b // ERROR
val i1: Int = b.toInt()
//sampleEnd
}
Все числовые типы поддерживают преобразования в другие типы:
toByte(): BytetoShort(): ShorttoInt(): InttoLong(): LongtoFloat(): FloattoDouble(): Double
Во многих случаях явные преобразования не нужны, потому что тип определяется из контекста, и арифметические операции перегружены для соответствующих преобразований, например:
val l = 1L + 3 // Long + Int => Long
Операции над числами
Kotlin поддерживает стандартный набор арифметических операций над числами: +, -, *, /, %. Они объявлены как члены соответствующих классов:
fun main() {
//sampleStart
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)
//sampleEnd
}
Вы также можете переопределить эти операторы для пользовательских классов. Подробнее см. Перегрузку операторов.
Деление целых чисел
Деление целых чисел всегда возвращает целое число. Любая дробная часть отбрасывается.
fun main() {
//sampleStart
val x = 5 / 2
//println(x == 2.5) // ERROR: Operator '==' cannot be applied to 'Int' and 'Double'
println(x == 2)
//sampleEnd
}
Это справедливо для деления любых двух целых типов:
fun main() {
//sampleStart
val x = 5L / 2
println(x == 2L)
//sampleEnd
}
Чтобы вернуть тип с плавающей запятой, явно преобразуйте один из аргументов в тип с плавающей запятой:
fun main() {
//sampleStart
val x = 5 / 2.toDouble()
println(x == 2.5)
//sampleEnd
}
Битовые операции
Kotlin предоставляет набор битовых операций над целыми числами. Они работают на битовом уровне напрямую с битами представления чисел. Битовые операции представлены функциями, которые могут быть вызваны в инфиксной форме. Они могут быть применены только к Int и Long:
val x = (1 shl 2) and 0x000FF000
Вот полный список битовых операций:
shl(bits)— сдвиг влево со знакомshr(bits)— сдвиг вправо со знакомushr(bits)— сдвиг вправо без знакаand(bits)— битовое Иor(bits)— битовое ИЛИxor(bits)— битовое ИСКЛЮЧАЮЩЕЕ ИЛИinv()— инверсия битов
Сравнение чисел с плавающей точкой
Операции над числами с плавающей точкой, обсуждаемые в этом разделе:
Проверки на равенство:
a == bиa != bОператоры сравнения:
a < b,a > b,a <= b,a >= bСоздание диапазона и проверки на диапазон:
a..b,x in a..b,x !in a..b
Когда операнды a и b статически известны как Float или Double или их варианты с nullable (тип объявлен или выведен, или является результатом умного преобразования типов), операции над числами и диапазоном, которые они образуют, следуют стандарту IEEE 754 для арифметики с плавающей точкой.
Однако, чтобы поддержать общие случаи использования и предоставить полное упорядочение, когда операнды не статически типизированы как числа с плавающей точкой (например, Any, Comparable<...>, параметр типа), операции используют реализации equals и compareTo для Float и Double, которые отличаются от стандарта, так что:
NaNсчитается равным самому себеNaNсчитается больше любого другого элемента, включаяPOSITIVE_INFINITY-0.0считается меньше0.0
© 2010–2023 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/numbers.html