Skip to content

数値

整数型

Kotlinには、数値を表現するための組み込み型が用意されています。 整数値には、サイズと値の範囲が異なる4つの型があります。

サイズ (ビット)最小値最大値
Byte8-128127
Short16-3276832767
Int32-2,147,483,648 (-231)2,147,483,647 (231 - 1)
Long64-9,223,372,036,854,775,808 (-263)9,223,372,036,854,775,807 (263 - 1)

符号付き整数型に加えて、Kotlinは符号なし整数型も提供しています。 符号なし整数は異なるユースケースを対象としているため、別個に説明します。 を参照してください。

明示的な型指定なしに変数を初期化すると、コンパイラは値を表現するのに十分な最小範囲の型をIntから自動的に推論します。Intの範囲を超えない場合、型はIntになります。その範囲を超える場合、型はLongになります。Long値を明示的に指定するには、値にサフィックスLを付加します。 ByteまたはShort型を使用するには、宣言で明示的に指定します。明示的な型指定は、指定された型の範囲を値が超えていないかをコンパイラにチェックさせます。

kotlin
val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

浮動小数点型

実数には、KotlinはIEEE 754標準に準拠する浮動小数点型FloatDoubleを提供します。 FloatはIEEE 754の_単精度_を反映し、Doubleは_倍精度_を反映します。

これらの型はサイズが異なり、異なる精度の浮動小数点数を格納できます。

サイズ (ビット)有効桁数 (ビット)指数部 (ビット)10進数の桁数
Float322486-7
Double64531115-16

DoubleおよびFloat変数は、小数部を持つ数値でのみ初期化できます。 小数部と整数部はピリオド (.) で区切ります。

小数値で初期化された変数に対しては、コンパイラはDouble型を推論します。

kotlin
val pi = 3.14          // Double

val one: Double = 1    // Int is inferred
// Initializer type mismatch

val oneDouble = 1.0    // Double

値に対して明示的にFloat型を指定するには、サフィックスfまたはFを追加します。 この方法で指定された値が10進数で7桁を超える場合、丸められます。

kotlin
val e = 2.7182818284          // Double
val eFloat = 2.7182818284f    // Float, actual value is 2.7182817

他のいくつかの言語とは異なり、Kotlinでは数値に対する暗黙的な拡大変換はありません。 たとえば、Doubleパラメータを持つ関数は、Double値でのみ呼び出すことができ、FloatInt、または他の数値では呼び出すことができません。

kotlin
fun main() {
    fun printDouble(x: Double) { print(x) }

    val x = 1.0
    val xInt = 1    
    val xFloat = 1.0f 

    printDouble(x)
    
    printDouble(xInt)   
    // Argument type mismatch
    
    printDouble(xFloat)
    // Argument type mismatch
}

数値型を異なる型に変換するには、明示的な数値変換を使用します。

数値リテラル

整数値にはいくつかの種類のリテラル定数があります。

  • 10進数: 123
  • 大文字のLで終わるLong型: 123L
  • 16進数: 0x0F
  • 2進数: 0b00001011

Kotlinでは8進数リテラルはサポートされていません。

Kotlinは浮動小数点数の従来の表記法もサポートしています。

  • Double型 (小数部が文字で終わらない場合のデフォルト): 123.5, 123.5e10
  • 文字fまたはFで終わるFloat型: 123.5f

数値定数を読みやすくするためにアンダースコアを使用できます。

kotlin
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
val bigFractional = 1_234_567.7182818284

符号なし整数リテラルには特殊なサフィックスもあります。 符号なし整数型のリテラルについて詳しくはこちらをご覧ください。

Java仮想マシンにおける数値のボックス化とキャッシュ

JVMが数値を格納する方法は、小さい (バイトサイズの) 数値にデフォルトで使用されるキャッシュのため、コードが直感に反する動作をする可能性があります。

JVMは数値をプリミティブ型 (intdoubleなど) として格納します。 ジェネリクスを使用したり、Int?のようなnull許容の数値参照を作成したりすると、数値はIntegerDoubleのようなJavaクラスにボックス化されます。

JVMは、Integer-128から127までの数値を表す他のオブジェクトにメモリ最適化手法を適用します。 そのようなオブジェクトへのすべてのnull許容参照は、同じキャッシュされたオブジェクトを参照します。 たとえば、以下のコードのnull許容オブジェクトは参照が等しくなります。

kotlin
fun main() {
    val a: Int = 100
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    
    println(boxedA === anotherBoxedA) // true
}

この範囲外の数値の場合、null許容オブジェクトは異なりますが、構造的に等しくなります。

kotlin
fun main() {
    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b
    
    println(boxedB === anotherBoxedB) // false
    println(boxedB == anotherBoxedB) // true
}

このため、Kotlinはボックス化可能な数値とリテラルとの参照等価性の使用について、以下のメッセージで警告します: "Identity equality for arguments of types ... and ... is prohibited."IntShortLongByte型 (およびCharBoolean) を比較する場合は、一貫した結果を得るために構造等価性チェックを使用してください。

明示的な数値変換

異なる表現のため、数値型は互いの_サブタイプではありません_。 結果として、より小さい型がより大きい型に暗黙的に変換されることは_なく_、その逆も同様です。 たとえば、Byte型の値をInt変数に代入するには、明示的な変換が必要です。

kotlin
fun main() {
    val byte: Byte = 1
    // OK, literals are checked statically
    
    val intAssignedByte: Int = byte 
    // Initializer type mismatch
    
    val intConvertedByte: Int = byte.toInt()
    
    println(intConvertedByte)
}

すべての数値型は、他の型への変換をサポートしています。

  • toByte(): Byte (FloatおよびDoubleでは非推奨)
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double

多くの場合、型がコンテキストから推論され、算術演算子が変換を自動的に処理するようにオーバーロードされているため、明示的な変換は必要ありません。例:

kotlin
fun main() {
    val l = 1L + 3       // Long + Int => Long
    println(l is Long)   // true
}

暗黙的な変換を行わない理由

Kotlinは暗黙的な変換をサポートしていません。これは、予期せぬ挙動につながる可能性があるためです。

異なる型の数値が暗黙的に変換された場合、等価性と同一性を静かに失うことがあります。 たとえば、IntLongのサブタイプであったと想像してみてください。

kotlin
// 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)      // Prints "false" as Long.equals() checks not only the value but whether the other number is Long as well

数値演算

Kotlinは数値に対する標準的な算術演算子 (+-*/%) をサポートしています。これらは適切なクラスのメンバーとして宣言されています。

kotlin
fun main() {
    println(1 + 2)
    println(2_500_000_000L - 1L)
    println(3.14 * 2.71)
    println(10.0 / 3)
}

これらの演算子は、カスタム数値クラスでオーバーライドできます。 詳細は演算子オーバーロードを参照してください。

整数除算

整数同士の除算は常に整数を返します。小数部はすべて破棄されます。

kotlin
fun main() {
    val x = 5 / 2
    println(x == 2.5) 
    // Operator '==' cannot be applied to 'Int' and 'Double'
    
    println(x == 2)   
    // true
}

これは、任意の2つの整数型間の除算に当てはまります。

kotlin
fun main() {
    val x = 5L / 2
    println (x == 2)
    // Error, as Long (x) cannot be compared to Int (2)
    
    println(x == 2L)
    // true
}

小数部を含む除算結果を返すには、引数のいずれかを明示的に浮動小数点型に変換します。

kotlin
fun main() {
    val x = 5 / 2.toDouble()
    println(x == 2.5)
}

ビット演算

Kotlinは整数に対する_ビット演算_のセットを提供します。これらは、数値の表現のビットと直接バイナリレベルで動作します。 ビット演算は、中置記法で呼び出すことができる関数として表現されます。これらはIntLongにのみ適用できます。

kotlin
fun main() {
    val x = 1
    val xShiftedLeft = (x shl 2)
    println(xShiftedLeft)  
    // 4
    
    val xAnd = x and 0x000FF000
    println(xAnd)          
    // 0
}

ビット演算の完全なリスト:

  • shl(bits) – 符号付き左シフト
  • shr(bits) – 符号付き右シフト
  • ushr(bits) – 符号なし右シフト
  • and(bits) – ビットごとのAND
  • or(bits) – ビットごとのOR
  • xor(bits) – ビットごとのXOR
  • inv() – ビットごとの反転

浮動小数点数の比較

このセクションで説明する浮動小数点数の演算は次のとおりです。

  • 等価性チェック: a == b および a != b
  • 比較演算子: a < ba > ba <= ba >= b
  • 範囲のインスタンス化と範囲チェック: a..bx in a..bx !in a..b

オペランドabFloatまたはDouble、あるいはそのnull許容の対応物 (型が宣言されているか、推論されているか、スマートキャストの結果である場合) であると静的に判明している場合、数値に対する演算とそれらが形成する範囲は、IEEE 754 浮動小数点算術標準に従います。

ただし、汎用的なユースケースをサポートし、全順序を提供するために、浮動小数点数として静的に型付けされていないオペランドについては動作が異なります。たとえば、AnyComparable<...>、またはCollection<T>型などです。この場合、演算はFloatおよびDoubleequalsおよびcompareToの実装を使用します。その結果、次のようになります。

  • NaNはそれ自身と等しいと見なされる
  • NaNPOSITIVE_INFINITYを含む他のどの要素よりも大きいと見なされる
  • -0.00.0よりも小さいと見なされる

ここでは、浮動小数点数として静的に型付けされたオペランド (Double.NaN) と、浮動小数点数として静的に型付けされていないオペランド (listOf(T)) の間の動作の違いを示す例を示します。

kotlin
fun main() {
    // Operand statically typed as floating-point number
    println(Double.NaN == Double.NaN)                 // false
    
    // Operand NOT statically typed as floating-point number
    // So NaN is equal to itself
    println(listOf(Double.NaN) == listOf(Double.NaN)) // true

    // Operand statically typed as floating-point number
    println(0.0 == -0.0)                              // true
    
    // Operand NOT statically typed as floating-point number
    // So -0.0 is less than 0.0
    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]
}