Числа
Целочисленные типы
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, а Double отражает двойную точность.
Эти типы отличаются по размеру и обеспечивают хранение чисел с плавающей точкой с различной точностью:
Тип |
Размер (биты) |
Значимые биты |
Биты порядка |
Десятичные цифры |
|---|---|---|---|---|
|
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
}
Для преобразования числовых значений в разные типы используйте явные преобразования.
Литеральные константы для чисел
Существуют следующие виды литеральных констант для целочисленных значений:
Десятичные:
123Для длинных значений используется заглавная буква
L:123LШестнадцатеричные:
0x0FДвоичные:
0b00001011
Kotlin также поддерживает обычную запись для чисел с плавающей точкой:
По умолчанию вещественные значения типа Double:
123.5,123.5e10Для чисел типа Float используется суффикс
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<...>, или Collection<T>. В этом случае операции используют реализации equals и compareTo для Float и Double. В результате:
NaNсчитается равным самому себеNaNсчитается больше любого другого элемента, включаяPOSITIVE_INFINITY-0.0считается меньше0.0
Вот пример, который демонстрирует разницу в поведении между операндами, статически типизированными как числа с плавающей точкой (Double.NaN) и операндами не статически типизированными как числа с плавающей точкой (listOf(T)).
fun main() {
//sampleStart
println(Double.NaN == Double.NaN) // false
println(listOf(Double.NaN) == listOf(Double.NaN)) // true
println(0.0 == -0.0) // true
println(listOf(0.0) == listOf(-0.0)) // false
println(listOf(Double.NaN, Double.POSITIVE_INFINITY, 0.0, -0.0).sorted())
// [-0.0, 0.0, Infinity, NaN]
//sampleEnd
}
© 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