Spec-Zone.ru › Kotlin 1.8

Числа

Целочисленные типы

Kotlin предоставляет набор встроенных типов, представляющих числа.
Для целых чисел существуют четыре типа с различными размерами и, следовательно, диапазонами значений:

Тип

Размер (биты)

Минимальное значение

Максимальное значение

Byte

8

-128

127

Short

16

-32768

32767

Int

32

-2 147 483 648 (-231)

2 147 483 647 (231 - 1)

Long

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.

Эти типы отличаются по размеру и обеспечивают хранение чисел с плавающей точкой с различной точностью:

Тип

Размер (биты)

Значимые биты

Биты экспоненты

Десятичные цифры

Float

32

24

8

6-7

Double

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

  • Longs помечаются заглавной буквой L: 123L

  • Шестнадцатеричные: 0x0F

  • Двоичные: 0b00001011

Восьмеричные литералы в Kotlin не поддерживаются.

Kotlin также поддерживает обычную запись для чисел с плавающей точкой:

  • Doubles по умолчанию: 123.5, 123.5e10

  • Floats помечаются 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(): Byte

  • toShort(): Short

  • toInt(): Int

  • toLong(): Long

  • toFloat(): Float

  • toDouble(): 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

Последнее изменение: 10 января 2023
Основные типы Булевы

© 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API