Spec-Zone.ru › Kotlin 1.7

Числа

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

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 одинарную точность, а Double отражает двойную точность.

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

Тип

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

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

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

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

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

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

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

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

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

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(): 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 августа 2022
Основные типы Булевы

© 2010–2022 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